xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 58332)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	6.10 (Berkeley) 03/01/93";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <sys/stat.h>
15 # include <sysexits.h>
16 /*
17 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
18 **
19 **	Runs through a string and strips off unquoted quote
20 **	characters and quote bits.  This is done in place.
21 **
22 **	Parameters:
23 **		s -- the string to strip.
24 **
25 **	Returns:
26 **		none.
27 **
28 **	Side Effects:
29 **		none.
30 **
31 **	Called By:
32 **		deliver
33 */
34 
35 stripquotes(s)
36 	char *s;
37 {
38 	register char *p;
39 	register char *q;
40 	register char c;
41 
42 	if (s == NULL)
43 		return;
44 
45 	p = q = s;
46 	do
47 	{
48 		c = *p++;
49 		if (c == '\\')
50 			c = *p++;
51 		else if (c == '"')
52 			continue;
53 		*q++ = c;
54 	} while (c != '\0');
55 }
56 /*
57 **  CAPITALIZE -- return a copy of a string, properly capitalized.
58 **
59 **	Parameters:
60 **		s -- the string to capitalize.
61 **
62 **	Returns:
63 **		a pointer to a properly capitalized string.
64 **
65 **	Side Effects:
66 **		none.
67 */
68 
69 char *
70 capitalize(s)
71 	register char *s;
72 {
73 	static char buf[50];
74 	register char *p;
75 
76 	p = buf;
77 
78 	for (;;)
79 	{
80 		while (!(isascii(*s) && isalpha(*s)) && *s != '\0')
81 			*p++ = *s++;
82 		if (*s == '\0')
83 			break;
84 		*p++ = toupper(*s);
85 		s++;
86 		while (isascii(*s) && isalpha(*s))
87 			*p++ = *s++;
88 	}
89 
90 	*p = '\0';
91 	return (buf);
92 }
93 /*
94 **  XALLOC -- Allocate memory and bitch wildly on failure.
95 **
96 **	THIS IS A CLUDGE.  This should be made to give a proper
97 **	error -- but after all, what can we do?
98 **
99 **	Parameters:
100 **		sz -- size of area to allocate.
101 **
102 **	Returns:
103 **		pointer to data region.
104 **
105 **	Side Effects:
106 **		Memory is allocated.
107 */
108 
109 char *
110 xalloc(sz)
111 	register int sz;
112 {
113 	register char *p;
114 
115 	p = malloc((unsigned) sz);
116 	if (p == NULL)
117 	{
118 		syserr("Out of memory!!");
119 		abort();
120 		/* exit(EX_UNAVAILABLE); */
121 	}
122 	return (p);
123 }
124 /*
125 **  COPYPLIST -- copy list of pointers.
126 **
127 **	This routine is the equivalent of newstr for lists of
128 **	pointers.
129 **
130 **	Parameters:
131 **		list -- list of pointers to copy.
132 **			Must be NULL terminated.
133 **		copycont -- if TRUE, copy the contents of the vector
134 **			(which must be a string) also.
135 **
136 **	Returns:
137 **		a copy of 'list'.
138 **
139 **	Side Effects:
140 **		none.
141 */
142 
143 char **
144 copyplist(list, copycont)
145 	char **list;
146 	bool copycont;
147 {
148 	register char **vp;
149 	register char **newvp;
150 
151 	for (vp = list; *vp != NULL; vp++)
152 		continue;
153 
154 	vp++;
155 
156 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
157 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
158 
159 	if (copycont)
160 	{
161 		for (vp = newvp; *vp != NULL; vp++)
162 			*vp = newstr(*vp);
163 	}
164 
165 	return (newvp);
166 }
167 /*
168 **  COPYQUEUE -- copy address queue.
169 **
170 **	This routine is the equivalent of newstr for address queues
171 **	addresses marked with QDONTSEND aren't copied
172 **
173 **	Parameters:
174 **		addr -- list of address structures to copy.
175 **
176 **	Returns:
177 **		a copy of 'addr'.
178 **
179 **	Side Effects:
180 **		none.
181 */
182 
183 ADDRESS *
184 copyqueue(addr)
185 	ADDRESS *addr;
186 {
187 	register ADDRESS *newaddr;
188 	ADDRESS *ret;
189 	register ADDRESS **tail = &ret;
190 
191 	while (addr != NULL)
192 	{
193 		if (!bitset(QDONTSEND, addr->q_flags))
194 		{
195 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
196 			STRUCTCOPY(*addr, *newaddr);
197 			*tail = newaddr;
198 			tail = &newaddr->q_next;
199 		}
200 		addr = addr->q_next;
201 	}
202 	*tail = NULL;
203 
204 	return ret;
205 }
206 /*
207 **  PRINTAV -- print argument vector.
208 **
209 **	Parameters:
210 **		av -- argument vector.
211 **
212 **	Returns:
213 **		none.
214 **
215 **	Side Effects:
216 **		prints av.
217 */
218 
219 printav(av)
220 	register char **av;
221 {
222 	while (*av != NULL)
223 	{
224 		if (tTd(0, 44))
225 			printf("\n\t%08x=", *av);
226 		else
227 			(void) putchar(' ');
228 		xputs(*av++);
229 	}
230 	(void) putchar('\n');
231 }
232 /*
233 **  LOWER -- turn letter into lower case.
234 **
235 **	Parameters:
236 **		c -- character to turn into lower case.
237 **
238 **	Returns:
239 **		c, in lower case.
240 **
241 **	Side Effects:
242 **		none.
243 */
244 
245 char
246 lower(c)
247 	register char c;
248 {
249 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
250 }
251 /*
252 **  XPUTS -- put string doing control escapes.
253 **
254 **	Parameters:
255 **		s -- string to put.
256 **
257 **	Returns:
258 **		none.
259 **
260 **	Side Effects:
261 **		output to stdout
262 */
263 
264 xputs(s)
265 	register char *s;
266 {
267 	register int c;
268 	register struct metamac *mp;
269 	extern struct metamac MetaMacros[];
270 
271 	if (s == NULL)
272 	{
273 		printf("<null>");
274 		return;
275 	}
276 	while ((c = (*s++ & 0377)) != '\0')
277 	{
278 		if (!isascii(c))
279 		{
280 			if (c == MATCHREPL || c == MACROEXPAND)
281 			{
282 				putchar('$');
283 				continue;
284 			}
285 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
286 			{
287 				if ((mp->metaval & 0377) == c)
288 				{
289 					printf("$%c", mp->metaname);
290 					break;
291 				}
292 			}
293 			if (mp->metaname != '\0')
294 				continue;
295 			(void) putchar('\\');
296 			c &= 0177;
297 		}
298 		if (isprint(c))
299 		{
300 			putchar(c);
301 			continue;
302 		}
303 
304 		/* wasn't a meta-macro -- find another way to print it */
305 		switch (c)
306 		{
307 		  case '\0':
308 			continue;
309 
310 		  case '\n':
311 			c = 'n';
312 			break;
313 
314 		  case '\r':
315 			c = 'r';
316 			break;
317 
318 		  case '\t':
319 			c = 't';
320 			break;
321 
322 		  default:
323 			(void) putchar('^');
324 			(void) putchar(c ^ 0100);
325 			continue;
326 		}
327 	}
328 	(void) fflush(stdout);
329 }
330 /*
331 **  MAKELOWER -- Translate a line into lower case
332 **
333 **	Parameters:
334 **		p -- the string to translate.  If NULL, return is
335 **			immediate.
336 **
337 **	Returns:
338 **		none.
339 **
340 **	Side Effects:
341 **		String pointed to by p is translated to lower case.
342 **
343 **	Called By:
344 **		parse
345 */
346 
347 makelower(p)
348 	register char *p;
349 {
350 	register char c;
351 
352 	if (p == NULL)
353 		return;
354 	for (; (c = *p) != '\0'; p++)
355 		if (isascii(c) && isupper(c))
356 			*p = tolower(c);
357 }
358 /*
359 **  BUILDFNAME -- build full name from gecos style entry.
360 **
361 **	This routine interprets the strange entry that would appear
362 **	in the GECOS field of the password file.
363 **
364 **	Parameters:
365 **		p -- name to build.
366 **		login -- the login name of this user (for &).
367 **		buf -- place to put the result.
368 **
369 **	Returns:
370 **		none.
371 **
372 **	Side Effects:
373 **		none.
374 */
375 
376 buildfname(gecos, login, buf)
377 	register char *gecos;
378 	char *login;
379 	char *buf;
380 {
381 	register char *p;
382 	register char *bp = buf;
383 	int l;
384 
385 	if (*gecos == '*')
386 		gecos++;
387 
388 	/* find length of final string */
389 	l = 0;
390 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
391 	{
392 		if (*p == '&')
393 			l += strlen(login);
394 		else
395 			l++;
396 	}
397 
398 	/* now fill in buf */
399 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
400 	{
401 		if (*p == '&')
402 		{
403 			(void) strcpy(bp, login);
404 			*bp = toupper(*bp);
405 			while (*bp != '\0')
406 				bp++;
407 		}
408 		else
409 			*bp++ = *p;
410 	}
411 	*bp = '\0';
412 }
413 /*
414 **  SAFEFILE -- return true if a file exists and is safe for a user.
415 **
416 **	Parameters:
417 **		fn -- filename to check.
418 **		uid -- uid to compare against.
419 **		mode -- mode bits that must match.
420 **
421 **	Returns:
422 **		0 if fn exists, is owned by uid, and matches mode.
423 **		An errno otherwise.  The actual errno is cleared.
424 **
425 **	Side Effects:
426 **		none.
427 */
428 
429 int
430 safefile(fn, uid, mode)
431 	char *fn;
432 	uid_t uid;
433 	int mode;
434 {
435 	struct stat stbuf;
436 
437 	if (stat(fn, &stbuf) < 0)
438 	{
439 		int ret = errno;
440 
441 		errno = 0;
442 		return ret;
443 	}
444 	if (stbuf.st_uid == uid && (stbuf.st_mode & mode) == mode)
445 		return 0;
446 	return EPERM;
447 }
448 /*
449 **  FIXCRLF -- fix <CR><LF> in line.
450 **
451 **	Looks for the <CR><LF> combination and turns it into the
452 **	UNIX canonical <NL> character.  It only takes one line,
453 **	i.e., it is assumed that the first <NL> found is the end
454 **	of the line.
455 **
456 **	Parameters:
457 **		line -- the line to fix.
458 **		stripnl -- if true, strip the newline also.
459 **
460 **	Returns:
461 **		none.
462 **
463 **	Side Effects:
464 **		line is changed in place.
465 */
466 
467 fixcrlf(line, stripnl)
468 	char *line;
469 	bool stripnl;
470 {
471 	register char *p;
472 
473 	p = strchr(line, '\n');
474 	if (p == NULL)
475 		return;
476 	if (p > line && p[-1] == '\r')
477 		p--;
478 	if (!stripnl)
479 		*p++ = '\n';
480 	*p = '\0';
481 }
482 /*
483 **  DFOPEN -- determined file open
484 **
485 **	This routine has the semantics of fopen, except that it will
486 **	keep trying a few times to make this happen.  The idea is that
487 **	on very loaded systems, we may run out of resources (inodes,
488 **	whatever), so this tries to get around it.
489 */
490 
491 FILE *
492 dfopen(filename, mode)
493 	char *filename;
494 	char *mode;
495 {
496 	register int tries;
497 	register FILE *fp;
498 
499 	for (tries = 0; tries < 10; tries++)
500 	{
501 		sleep((unsigned) (10 * tries));
502 		errno = 0;
503 		fp = fopen(filename, mode);
504 		if (fp != NULL)
505 			break;
506 		if (errno != ENFILE && errno != EINTR)
507 			break;
508 	}
509 	if (fp != NULL)
510 	{
511 #ifdef FLOCK
512 		int locktype;
513 
514 		/* lock the file to avoid accidental conflicts */
515 		if (*mode == 'w' || *mode == 'a')
516 			locktype = LOCK_EX;
517 		else
518 			locktype = LOCK_SH;
519 		(void) flock(fileno(fp), locktype);
520 #endif
521 		errno = 0;
522 	}
523 	return (fp);
524 }
525 /*
526 **  PUTLINE -- put a line like fputs obeying SMTP conventions
527 **
528 **	This routine always guarantees outputing a newline (or CRLF,
529 **	as appropriate) at the end of the string.
530 **
531 **	Parameters:
532 **		l -- line to put.
533 **		fp -- file to put it onto.
534 **		m -- the mailer used to control output.
535 **
536 **	Returns:
537 **		none
538 **
539 **	Side Effects:
540 **		output of l to fp.
541 */
542 
543 putline(l, fp, m)
544 	register char *l;
545 	FILE *fp;
546 	MAILER *m;
547 {
548 	register char *p;
549 	register char svchar;
550 
551 	/* strip out 0200 bits -- these can look like TELNET protocol */
552 	if (bitnset(M_7BITS, m->m_flags))
553 	{
554 		for (p = l; svchar = *p; ++p)
555 			if (svchar & 0200)
556 				*p = svchar &~ 0200;
557 	}
558 
559 	do
560 	{
561 		/* find the end of the line */
562 		p = strchr(l, '\n');
563 		if (p == NULL)
564 			p = &l[strlen(l)];
565 
566 		/* check for line overflow */
567 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
568 		{
569 			register char *q = &l[m->m_linelimit - 1];
570 
571 			svchar = *q;
572 			*q = '\0';
573 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
574 				(void) putc('.', fp);
575 			fputs(l, fp);
576 			(void) putc('!', fp);
577 			fputs(m->m_eol, fp);
578 			*q = svchar;
579 			l = q;
580 		}
581 
582 		/* output last part */
583 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
584 			(void) putc('.', fp);
585 		for ( ; l < p; ++l)
586 			(void) putc(*l, fp);
587 		fputs(m->m_eol, fp);
588 		if (*l == '\n')
589 			++l;
590 	} while (l[0] != '\0');
591 }
592 /*
593 **  XUNLINK -- unlink a file, doing logging as appropriate.
594 **
595 **	Parameters:
596 **		f -- name of file to unlink.
597 **
598 **	Returns:
599 **		none.
600 **
601 **	Side Effects:
602 **		f is unlinked.
603 */
604 
605 xunlink(f)
606 	char *f;
607 {
608 	register int i;
609 
610 # ifdef LOG
611 	if (LogLevel > 98)
612 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
613 # endif /* LOG */
614 
615 	i = unlink(f);
616 # ifdef LOG
617 	if (i < 0 && LogLevel > 97)
618 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
619 # endif /* LOG */
620 }
621 /*
622 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
623 **
624 **	Parameters:
625 **		buf -- place to put the input line.
626 **		siz -- size of buf.
627 **		fp -- file to read from.
628 **		timeout -- the timeout before error occurs.
629 **
630 **	Returns:
631 **		NULL on error (including timeout).  This will also leave
632 **			buf containing a null string.
633 **		buf otherwise.
634 **
635 **	Side Effects:
636 **		none.
637 */
638 
639 static jmp_buf	CtxReadTimeout;
640 
641 char *
642 sfgets(buf, siz, fp, timeout)
643 	char *buf;
644 	int siz;
645 	FILE *fp;
646 	time_t timeout;
647 {
648 	register EVENT *ev = NULL;
649 	register char *p;
650 	static int readtimeout();
651 
652 	/* set the timeout */
653 	if (timeout != 0)
654 	{
655 		if (setjmp(CtxReadTimeout) != 0)
656 		{
657 # ifdef LOG
658 			syslog(LOG_NOTICE,
659 			    "timeout waiting for input from %s\n",
660 			    CurHostName? CurHostName: "local");
661 # endif
662 			errno = 0;
663 			usrerr("451 timeout waiting for input");
664 			buf[0] = '\0';
665 			return (NULL);
666 		}
667 		ev = setevent(timeout, readtimeout, 0);
668 	}
669 
670 	/* try to read */
671 	p = NULL;
672 	while (p == NULL && !feof(fp) && !ferror(fp))
673 	{
674 		errno = 0;
675 		p = fgets(buf, siz, fp);
676 		if (errno == EINTR)
677 			clearerr(fp);
678 	}
679 
680 	/* clear the event if it has not sprung */
681 	clrevent(ev);
682 
683 	/* clean up the books and exit */
684 	LineNumber++;
685 	if (p == NULL)
686 	{
687 		buf[0] = '\0';
688 		return (NULL);
689 	}
690 	if (!EightBit)
691 		for (p = buf; *p != '\0'; p++)
692 			*p &= ~0200;
693 	return (buf);
694 }
695 
696 static
697 readtimeout()
698 {
699 	longjmp(CtxReadTimeout, 1);
700 }
701 /*
702 **  FGETFOLDED -- like fgets, but know about folded lines.
703 **
704 **	Parameters:
705 **		buf -- place to put result.
706 **		n -- bytes available.
707 **		f -- file to read from.
708 **
709 **	Returns:
710 **		input line(s) on success, NULL on error or EOF.
711 **		This will normally be buf -- unless the line is too
712 **			long, when it will be xalloc()ed.
713 **
714 **	Side Effects:
715 **		buf gets lines from f, with continuation lines (lines
716 **		with leading white space) appended.  CRLF's are mapped
717 **		into single newlines.  Any trailing NL is stripped.
718 */
719 
720 char *
721 fgetfolded(buf, n, f)
722 	char *buf;
723 	register int n;
724 	FILE *f;
725 {
726 	register char *p = buf;
727 	char *bp = buf;
728 	register int i;
729 
730 	n--;
731 	while ((i = getc(f)) != EOF)
732 	{
733 		if (i == '\r')
734 		{
735 			i = getc(f);
736 			if (i != '\n')
737 			{
738 				if (i != EOF)
739 					(void) ungetc(i, f);
740 				i = '\r';
741 			}
742 		}
743 		if (--n <= 0)
744 		{
745 			/* allocate new space */
746 			char *nbp;
747 			int nn;
748 
749 			nn = (p - bp);
750 			if (nn < MEMCHUNKSIZE)
751 				nn *= 2;
752 			else
753 				nn += MEMCHUNKSIZE;
754 			nbp = xalloc(nn);
755 			bcopy(bp, nbp, p - bp);
756 			p = &nbp[p - bp];
757 			if (bp != buf)
758 				free(bp);
759 			bp = nbp;
760 			n = nn - (p - bp);
761 		}
762 		*p++ = i;
763 		if (i == '\n')
764 		{
765 			LineNumber++;
766 			i = getc(f);
767 			if (i != EOF)
768 				(void) ungetc(i, f);
769 			if (i != ' ' && i != '\t')
770 				break;
771 		}
772 	}
773 	if (p == bp)
774 		return (NULL);
775 	*--p = '\0';
776 	return (bp);
777 }
778 /*
779 **  CURTIME -- return current time.
780 **
781 **	Parameters:
782 **		none.
783 **
784 **	Returns:
785 **		the current time.
786 **
787 **	Side Effects:
788 **		none.
789 */
790 
791 time_t
792 curtime()
793 {
794 	auto time_t t;
795 
796 	(void) time(&t);
797 	return (t);
798 }
799 /*
800 **  ATOBOOL -- convert a string representation to boolean.
801 **
802 **	Defaults to "TRUE"
803 **
804 **	Parameters:
805 **		s -- string to convert.  Takes "tTyY" as true,
806 **			others as false.
807 **
808 **	Returns:
809 **		A boolean representation of the string.
810 **
811 **	Side Effects:
812 **		none.
813 */
814 
815 bool
816 atobool(s)
817 	register char *s;
818 {
819 	if (*s == '\0' || strchr("tTyY", *s) != NULL)
820 		return (TRUE);
821 	return (FALSE);
822 }
823 /*
824 **  ATOOCT -- convert a string representation to octal.
825 **
826 **	Parameters:
827 **		s -- string to convert.
828 **
829 **	Returns:
830 **		An integer representing the string interpreted as an
831 **		octal number.
832 **
833 **	Side Effects:
834 **		none.
835 */
836 
837 atooct(s)
838 	register char *s;
839 {
840 	register int i = 0;
841 
842 	while (*s >= '0' && *s <= '7')
843 		i = (i << 3) | (*s++ - '0');
844 	return (i);
845 }
846 /*
847 **  WAITFOR -- wait for a particular process id.
848 **
849 **	Parameters:
850 **		pid -- process id to wait for.
851 **
852 **	Returns:
853 **		status of pid.
854 **		-1 if pid never shows up.
855 **
856 **	Side Effects:
857 **		none.
858 */
859 
860 waitfor(pid)
861 	int pid;
862 {
863 	auto int st;
864 	int i;
865 
866 	do
867 	{
868 		errno = 0;
869 		i = wait(&st);
870 	} while ((i >= 0 || errno == EINTR) && i != pid);
871 	if (i < 0)
872 		st = -1;
873 	return (st);
874 }
875 /*
876 **  BITINTERSECT -- tell if two bitmaps intersect
877 **
878 **	Parameters:
879 **		a, b -- the bitmaps in question
880 **
881 **	Returns:
882 **		TRUE if they have a non-null intersection
883 **		FALSE otherwise
884 **
885 **	Side Effects:
886 **		none.
887 */
888 
889 bool
890 bitintersect(a, b)
891 	BITMAP a;
892 	BITMAP b;
893 {
894 	int i;
895 
896 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
897 		if ((a[i] & b[i]) != 0)
898 			return (TRUE);
899 	return (FALSE);
900 }
901 /*
902 **  BITZEROP -- tell if a bitmap is all zero
903 **
904 **	Parameters:
905 **		map -- the bit map to check
906 **
907 **	Returns:
908 **		TRUE if map is all zero.
909 **		FALSE if there are any bits set in map.
910 **
911 **	Side Effects:
912 **		none.
913 */
914 
915 bool
916 bitzerop(map)
917 	BITMAP map;
918 {
919 	int i;
920 
921 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
922 		if (map[i] != 0)
923 			return (FALSE);
924 	return (TRUE);
925 }
926 /*
927 **  STRCONTAINEDIN -- tell if one string is contained in another
928 **
929 **	Parameters:
930 **		a -- possible substring.
931 **		b -- possible superstring.
932 **
933 **	Returns:
934 **		TRUE if a is contained in b.
935 **		FALSE otherwise.
936 */
937 
938 bool
939 strcontainedin(a, b)
940 	register char *a;
941 	register char *b;
942 {
943 	int l;
944 
945 	l = strlen(a);
946 	for (;;)
947 	{
948 		b = strchr(b, a[0]);
949 		if (b == NULL)
950 			return FALSE;
951 		if (strncmp(a, b, l) == 0)
952 			return TRUE;
953 		b++;
954 	}
955 }
956 /*
957 **  TRANSIENTERROR -- tell if an error code indicates a transient failure
958 **
959 **	This looks at an errno value and tells if this is likely to
960 **	go away if retried later.
961 **
962 **	Parameters:
963 **		err -- the errno code to classify.
964 **
965 **	Returns:
966 **		TRUE if this is probably transient.
967 **		FALSE otherwise.
968 */
969 
970 bool
971 transienterror(err)
972 	int err;
973 {
974 	switch (err)
975 	{
976 	  case EIO:			/* I/O error */
977 	  case ENXIO:			/* Device not configured */
978 	  case EAGAIN:			/* Resource temporarily unavailable */
979 	  case ENOMEM:			/* Cannot allocate memory */
980 	  case ENODEV:			/* Operation not supported by device */
981 	  case ENFILE:			/* Too many open files in system */
982 	  case EMFILE:			/* Too many open files */
983 	  case ENOSPC:			/* No space left on device */
984 #ifdef ETIMEDOUT
985 	  case ETIMEDOUT:		/* Connection timed out */
986 #endif
987 #ifdef ESTALE
988 	  case ESTALE:			/* Stale NFS file handle */
989 #endif
990 #ifdef ENETDOWN
991 	  case ENETDOWN:		/* Network is down */
992 #endif
993 #ifdef ENETUNREACH
994 	  case ENETUNREACH:		/* Network is unreachable */
995 #endif
996 #ifdef ENETRESET
997 	  case ENETRESET:		/* Network dropped connection on reset */
998 #endif
999 #ifdef ECONNABORTED
1000 	  case ECONNABORTED:		/* Software caused connection abort */
1001 #endif
1002 #ifdef ECONNRESET
1003 	  case ECONNRESET:		/* Connection reset by peer */
1004 #endif
1005 #ifdef ENOBUFS
1006 	  case ENOBUFS:			/* No buffer space available */
1007 #endif
1008 #ifdef ESHUTDOWN
1009 	  case ESHUTDOWN:		/* Can't send after socket shutdown */
1010 #endif
1011 #ifdef ECONNREFUSED
1012 	  case ECONNREFUSED:		/* Connection refused */
1013 #endif
1014 #ifdef EHOSTDOWN
1015 	  case EHOSTDOWN:		/* Host is down */
1016 #endif
1017 #ifdef EHOSTUNREACH
1018 	  case EHOSTUNREACH:		/* No route to host */
1019 #endif
1020 #ifdef EDQUOT
1021 	  case EDQUOT:			/* Disc quota exceeded */
1022 #endif
1023 #ifdef EPROCLIM
1024 	  case EPROCLIM:		/* Too many processes */
1025 #endif
1026 #ifdef EUSERS
1027 	  case EUSERS:			/* Too many users */
1028 #endif
1029 #ifdef EDEADLK
1030 	  case EDEADLK:			/* Resource deadlock avoided */
1031 #endif
1032 		return TRUE;
1033 	}
1034 
1035 	/* nope, must be permanent */
1036 	return FALSE;
1037 }
1038