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