vacation.c
上传用户:xu_441
上传日期:2007-01-04
资源大小:1640k
文件大小:16k
源码类别:

Email客户端

开发平台:

Unix_Linux

  1. /*
  2.  * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
  3.  * All rights reserved.
  4.  * Copyright (c) 1983, 1987, 1993
  5.  * The Regents of the University of California.  All rights reserved.
  6.  * Copyright (c) 1983 Eric P. Allman.  All rights reserved.
  7.  *
  8.  * By using this file, you agree to the terms and conditions set
  9.  * forth in the LICENSE file which can be found at the top level of
  10.  * the sendmail distribution.
  11.  *
  12.  */
  13. #ifndef lint
  14. static char copyright[] =
  15. "@(#) Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.n
  16. All rights reserved.n
  17.      Copyright (c) 1983, 1987, 1993n
  18. The Regents of the University of California.  All rights reserved.n
  19.      Copyright (c) 1983 Eric P. Allman.  All rights reserved.n";
  20. #endif /* ! lint */
  21. #ifndef lint
  22. static char id[] = "@(#)$Id: vacation.c,v 8.57 1999/11/07 05:26:18 gshapiro Exp $";
  23. #endif /* ! lint */
  24. #include <ctype.h>
  25. #include <stdlib.h>
  26. #include <syslog.h>
  27. #include <time.h>
  28. #include <unistd.h>
  29. #ifdef EX_OK
  30. # undef EX_OK /* unistd.h may have another use for this */
  31. #endif /* EX_OK */
  32. #include <sysexits.h>
  33. #if defined(sun) && !defined(BSD) && !defined(SOLARIS)
  34. # include <pathnames.h>
  35. #endif /* sun && ! BSD && ! SOLARIS */
  36. #include "sendmail/sendmail.h"
  37. #include "libsmdb/smdb.h"
  38. #if defined(__hpux) && !defined(HPUX11)
  39. # undef syslog /* Undo hard_syslog conf.h change */
  40. #endif /* defined(__hpux) && !defined(HPUX11) */
  41. #ifndef _PATH_SENDMAIL
  42. # define _PATH_SENDMAIL "/usr/lib/sendmail"
  43. #endif /* ! _PATH_SENDMAIL */
  44. #define ONLY_ONCE ((time_t) 0) /* send at most one reply */
  45. #define INTERVAL_UNDEF ((time_t) (-1)) /* no value given */
  46. uid_t RealUid;
  47. gid_t RealGid;
  48. char *RealUserName;
  49. uid_t RunAsUid;
  50. uid_t RunAsGid;
  51. char *RunAsUserName;
  52. int Verbose = 2;
  53. bool DontInitGroups = FALSE;
  54. uid_t TrustedUid = 0;
  55. BITMAP256 DontBlameSendmail;
  56. /*
  57. **  VACATION -- return a message to the sender when on vacation.
  58. **
  59. ** This program is invoked as a message receiver.  It returns a
  60. ** message specified by the user to whomever sent the mail, taking
  61. ** care not to return a message too often to prevent "I am on
  62. ** vacation" loops.
  63. */
  64. #define VDB ".vacation" /* vacation database */
  65. #define VMSG ".vacation.msg" /* vacation message */
  66. #define SECSPERDAY (60 * 60 * 24)
  67. #define DAYSPERWEEK 7
  68. #ifndef TRUE
  69. # define TRUE 1
  70. # define FALSE 0
  71. #endif /* ! TRUE */
  72. #ifndef __P
  73. # ifdef __STDC__
  74. #  define __P(protos) protos
  75. # else /* __STDC__ */
  76. #  define __P(protos) ()
  77. #  define const
  78. # endif /* __STDC__ */
  79. #endif /* ! __P */
  80. typedef struct alias
  81. {
  82. char *name;
  83. struct alias *next;
  84. } ALIAS;
  85. ALIAS *Names = NULL;
  86. SMDB_DATABASE *Db;
  87. char From[MAXLINE];
  88. int
  89. main(argc, argv)
  90. int argc;
  91. char **argv;
  92. {
  93. bool iflag, emptysender, exclude;
  94. int ch;
  95. int result;
  96. time_t interval;
  97. struct passwd *pw;
  98. ALIAS *cur;
  99. char *dbfilename = VDB;
  100. char *msgfilename = VMSG;
  101. SMDB_USER_INFO user_info;
  102. static char rnamebuf[MAXNAME];
  103. extern int optind, opterr;
  104. extern char *optarg;
  105. extern void usage __P((void));
  106. extern void setinterval __P((time_t));
  107. extern void readheaders __P((void));
  108. extern bool recent __P((void));
  109. extern void setreply __P((char *, time_t));
  110. extern void sendmessage __P((char *, char *, bool));
  111. extern void xclude __P((FILE *));
  112. /* Vars needed to link with smutil */
  113. clrbitmap(DontBlameSendmail);
  114. RunAsUid = RealUid = getuid();
  115. RunAsGid = RealGid = getgid();
  116. pw = getpwuid(RealUid);
  117. if (pw != NULL)
  118. {
  119. if (strlen(pw->pw_name) > MAXNAME - 1)
  120. pw->pw_name[MAXNAME] = '';
  121. snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
  122. }
  123. else
  124. snprintf(rnamebuf, sizeof rnamebuf,
  125.  "Unknown UID %d", (int) RealUid);
  126. RunAsUserName = RealUserName = rnamebuf;
  127. opterr = 0;
  128. iflag = FALSE;
  129. emptysender = FALSE;
  130. exclude = FALSE;
  131. interval = INTERVAL_UNDEF;
  132. *From = '';
  133. while ((ch = getopt(argc, argv, "a:f:Iim:r:s:xz")) != -1)
  134. {
  135. switch((char)ch)
  136. {
  137.   case 'a': /* alias */
  138. cur = (ALIAS *)malloc((u_int)sizeof(ALIAS));
  139. if (cur == NULL)
  140. {
  141. syslog(LOG_NOTICE,
  142.        "vacation: can't allocate memory for alias %s.n",
  143.        optarg);
  144. break;
  145. }
  146. cur->name = optarg;
  147. cur->next = Names;
  148. Names = cur;
  149. break;
  150.   case 'f': /* alternate database */
  151. dbfilename = optarg;
  152. break;
  153.   case 'I': /* backward compatible */
  154.   case 'i': /* init the database */
  155. iflag = TRUE;
  156. break;
  157.   case 'm': /* alternate message file */
  158. msgfilename = optarg;
  159. break;
  160.   case 'r':
  161. if (isascii(*optarg) && isdigit(*optarg))
  162. {
  163. interval = atol(optarg) * SECSPERDAY;
  164. if (interval < 0)
  165. usage();
  166. }
  167. else
  168. interval = ONLY_ONCE;
  169. break;
  170.   case 's': /* alternate sender name */
  171. (void) strlcpy(From, optarg, sizeof From);
  172. break;
  173.   case 'x':
  174. exclude = TRUE;
  175. break;
  176.   case 'z':
  177. emptysender = TRUE;
  178. break;
  179.   case '?':
  180.   default:
  181. usage();
  182. break;
  183. }
  184. }
  185. argc -= optind;
  186. argv += optind;
  187. if (argc != 1)
  188. {
  189. if (!iflag && !exclude)
  190. usage();
  191. if ((pw = getpwuid(getuid())) == NULL)
  192. {
  193. syslog(LOG_ERR,
  194.        "vacation: no such user uid %u.n", getuid());
  195. exit(EX_NOUSER);
  196. }
  197. }
  198. else if ((pw = getpwnam(*argv)) == NULL)
  199. {
  200. syslog(LOG_ERR, "vacation: no such user %s.n", *argv);
  201. exit(EX_NOUSER);
  202. }
  203. if (chdir(pw->pw_dir) != 0)
  204. {
  205. syslog(LOG_NOTICE,
  206.        "vacation: no such directory %s.n", pw->pw_dir);
  207. exit(EX_NOINPUT);
  208. }
  209. user_info.smdbu_id = pw->pw_uid;
  210. user_info.smdbu_group_id = pw->pw_gid;
  211. (void) strlcpy(user_info.smdbu_name, pw->pw_name,
  212.        SMDB_MAX_USER_NAME_LEN);
  213. result = smdb_open_database(&Db, dbfilename,
  214.     O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
  215.     S_IRUSR|S_IWUSR, SFF_CREAT,
  216.     SMDB_TYPE_DEFAULT, &user_info, NULL);
  217. if (result != SMDBE_OK)
  218. {
  219. syslog(LOG_NOTICE, "vacation: %s: %sn", dbfilename,
  220.        errstring(errno));
  221. exit(EX_DATAERR);
  222. }
  223. if (interval != INTERVAL_UNDEF)
  224. setinterval(interval);
  225. if (iflag)
  226. {
  227. result = Db->smdb_close(Db);
  228. if (!exclude)
  229. exit(EX_OK);
  230. }
  231. if (exclude)
  232. {
  233. xclude(stdin);
  234. result = Db->smdb_close(Db);
  235. exit(EX_OK);
  236. }
  237. if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL)
  238. {
  239. syslog(LOG_NOTICE,
  240.        "vacation: can't allocate memory for username.n");
  241. exit(EX_OSERR);
  242. }
  243. cur->name = pw->pw_name;
  244. cur->next = Names;
  245. Names = cur;
  246. readheaders();
  247. if (!recent())
  248. {
  249. time_t now;
  250. (void) time(&now);
  251. setreply(From, now);
  252. result = Db->smdb_close(Db);
  253. sendmessage(pw->pw_name, msgfilename, emptysender);
  254. }
  255. else
  256. result = Db->smdb_close(Db);
  257. exit(EX_OK);
  258. /* NOTREACHED */
  259. return EX_OK;
  260. }
  261. /*
  262. ** READHEADERS -- read mail headers
  263. **
  264. ** Parameters:
  265. ** none.
  266. **
  267. ** Returns:
  268. ** nothing.
  269. **
  270. */
  271. void
  272. readheaders()
  273. {
  274. bool tome, cont;
  275. register char *p;
  276. register ALIAS *cur;
  277. char buf[MAXLINE];
  278. extern bool junkmail __P((char *));
  279. extern bool nsearch __P((char *, char *));
  280. cont = tome = FALSE;
  281. while (!tome && fgets(buf, sizeof(buf), stdin) && *buf != 'n')
  282. {
  283. switch(*buf)
  284. {
  285.   case 'F': /* "From " */
  286. cont = FALSE;
  287. if (strncmp(buf, "From ", 5) == 0)
  288. {
  289. bool quoted = FALSE;
  290. p = buf + 5;
  291. while (*p != '')
  292. {
  293. /* escaped character */
  294. if (*p == '\')
  295. {
  296. p++;
  297. if (*p == '')
  298. {
  299. syslog(LOG_NOTICE,
  300.        "vacation: badly formatted "From " line.n");
  301. exit(EX_DATAERR);
  302. }
  303. }
  304. else if (*p == '"')
  305. quoted = !quoted;
  306. else if (*p == 'r' || *p == 'n')
  307. break;
  308. else if (*p == ' ' && !quoted)
  309. break;
  310. p++;
  311. }
  312. if (quoted)
  313. {
  314. syslog(LOG_NOTICE,
  315.        "vacation: badly formatted "From " line.n");
  316. exit(EX_DATAERR);
  317. }
  318. *p = '';
  319. /* ok since both strings have MAXLINE length */
  320. if (*From == '')
  321. (void)strlcpy(From, buf + 5,
  322.       sizeof From);
  323. if ((p = strchr(buf + 5, 'n')) != NULL)
  324. *p = '';
  325. if (junkmail(buf + 5))
  326. exit(EX_OK);
  327. }
  328. break;
  329. case 'P': /* "Precedence:" */
  330. cont = FALSE;
  331. if (strlen(buf) <= 10 ||
  332.     strncasecmp(buf, "Precedence", 10) != 0 ||
  333.     (buf[10] != ':' && buf[10] != ' ' &&
  334.      buf[10] != 't'))
  335. break;
  336. if ((p = strchr(buf, ':')) == NULL)
  337. break;
  338. while (*++p != '' && isascii(*p) && isspace(*p));
  339. if (*p == '')
  340. break;
  341. if (strncasecmp(p, "junk", 4) == 0 ||
  342.     strncasecmp(p, "bulk", 4) == 0 ||
  343.     strncasecmp(p, "list", 4) == 0)
  344. exit(EX_OK);
  345. break;
  346. case 'C': /* "Cc:" */
  347. if (strncmp(buf, "Cc:", 3) != 0)
  348. break;
  349. cont = TRUE;
  350. goto findme;
  351. case 'T': /* "To:" */
  352. if (strncmp(buf, "To:", 3) != 0)
  353. break;
  354. cont = TRUE;
  355. goto findme;
  356. default:
  357. if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
  358. {
  359. cont = FALSE;
  360. break;
  361. }
  362. findme:
  363. for (cur = Names;
  364.      !tome && cur != NULL;
  365.      cur = cur->next)
  366. tome = nsearch(cur->name, buf);
  367. }
  368. }
  369. if (!tome)
  370. exit(EX_OK);
  371. if (*From == '')
  372. {
  373. syslog(LOG_NOTICE, "vacation: no initial "From " line.n");
  374. exit(EX_DATAERR);
  375. }
  376. }
  377. /*
  378. ** NSEARCH --
  379. ** do a nice, slow, search of a string for a substring.
  380. **
  381. ** Parameters:
  382. ** name -- name to search.
  383. ** str -- string in which to search.
  384. **
  385. ** Returns:
  386. ** is name a substring of str?
  387. **
  388. */
  389. bool
  390. nsearch(name, str)
  391. register char *name, *str;
  392. {
  393. register size_t len;
  394. register char *s;
  395. len = strlen(name);
  396. for (s = str; *s != ''; ++s)
  397. {
  398. /*
  399. **  Check to make sure that the string matches and
  400. **  the previous character is not an alphanumeric and
  401. **  the next character after the match is not an alphanumeric.
  402. **
  403. **  This prevents matching "eric" to "derick" while still
  404. **  matching "eric" to "<eric+detail>".
  405. */
  406. if (tolower(*s) == tolower(*name) &&
  407.     strncasecmp(name, s, len) == 0 &&
  408.     (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
  409.     (!isascii(*(s + len)) || !isalnum(*(s + len))))
  410. return TRUE;
  411. }
  412. return FALSE;
  413. }
  414. /*
  415. ** JUNKMAIL --
  416. ** read the header and return if automagic/junk/bulk/list mail
  417. **
  418. ** Parameters:
  419. ** from -- sender address.
  420. **
  421. ** Returns:
  422. ** is this some automated/junk/bulk/list mail?
  423. **
  424. */
  425. bool
  426. junkmail(from)
  427. char *from;
  428. {
  429. register size_t len;
  430. register char *p;
  431. register struct ignore *cur;
  432. static struct ignore
  433. {
  434. char *name;
  435. size_t len;
  436. } ignore[] =
  437. {
  438. { "-request", 8 },
  439. { "postmaster", 10 },
  440. { "uucp", 4 },
  441. { "mailer-daemon", 13 },
  442. { "mailer", 6 },
  443. { "-relay", 6 },
  444. { NULL, 0 }
  445. };
  446. /*
  447.  * This is mildly amusing, and I'm not positive it's right; trying
  448.  * to find the "real" name of the sender, assuming that addresses
  449.  * will be some variant of:
  450.  *
  451.  * From site!site!SENDER%site.domain%site.domain@site.domain
  452.  */
  453. if ((p = strchr(from, '%')) == NULL &&
  454.     (p = strchr(from, '@')) == NULL)
  455. {
  456. if ((p = strrchr(from, '!')) != NULL)
  457. ++p;
  458. else
  459. p = from;
  460. for (; *p; ++p)
  461. continue;
  462. }
  463. len = p - from;
  464. for (cur = ignore; cur->name != NULL; ++cur)
  465. {
  466. if (len >= cur->len &&
  467.     strncasecmp(cur->name, p - cur->len, cur->len) == 0)
  468. return TRUE;
  469. }
  470. return FALSE;
  471. }
  472. #define VIT "__VACATION__INTERVAL__TIMER__"
  473. /*
  474. ** RECENT --
  475. ** find out if user has gotten a vacation message recently.
  476. **
  477. ** Parameters:
  478. ** none.
  479. **
  480. ** Returns:
  481. ** TRUE iff user has gotten a vacation message recently.
  482. **
  483. */
  484. bool
  485. recent()
  486. {
  487. SMDB_DBENT key, data;
  488. time_t then, next;
  489. bool trydomain = FALSE;
  490. int st;
  491. char *domain;
  492. memset(&key, '', sizeof key);
  493. memset(&data, '', sizeof data);
  494. /* get interval time */
  495. key.data.data = VIT;
  496. key.data.size = sizeof(VIT);
  497. st = Db->smdb_get(Db, &key, &data, 0);
  498. if (st != SMDBE_OK)
  499. next = SECSPERDAY * DAYSPERWEEK;
  500. else
  501. memmove(&next, data.data.data, sizeof(next));
  502. memset(&data, '', sizeof data);
  503. /* get record for this address */
  504. key.data.data = From;
  505. key.data.size = strlen(From);
  506. do
  507. {
  508. st = Db->smdb_get(Db, &key, &data, 0);
  509. if (st == SMDBE_OK)
  510. {
  511. memmove(&then, data.data.data, sizeof(then));
  512. if (next == ONLY_ONCE || then == ONLY_ONCE ||
  513.     then + next > time(NULL))
  514. return TRUE;
  515. }
  516. if ((trydomain = !trydomain) &&
  517.     (domain = strchr(From, '@')) != NULL)
  518. {
  519. key.data.data = domain;
  520. key.data.size = strlen(domain);
  521. }
  522. } while (trydomain);
  523. return FALSE;
  524. }
  525. /*
  526. ** SETINTERVAL --
  527. ** store the reply interval
  528. **
  529. ** Parameters:
  530. ** interval -- time interval for replies.
  531. **
  532. ** Returns:
  533. ** nothing.
  534. **
  535. ** Side Effects:
  536. ** stores the reply interval in database.
  537. */
  538. void
  539. setinterval(interval)
  540. time_t interval;
  541. {
  542. SMDB_DBENT key, data;
  543. memset(&key, '', sizeof key);
  544. memset(&data, '', sizeof data);
  545. key.data.data = VIT;
  546. key.data.size = sizeof(VIT);
  547. data.data.data = (char*) &interval;
  548. data.data.size = sizeof(interval);
  549. (void)(Db->smdb_put)(Db, &key, &data, 0);
  550. }
  551. /*
  552. ** SETREPLY --
  553. ** store that this user knows about the vacation.
  554. **
  555. ** Parameters:
  556. ** from -- sender address.
  557. ** when -- last reply time.
  558. **
  559. ** Returns:
  560. ** nothing.
  561. **
  562. ** Side Effects:
  563. ** stores user/time in database.
  564. */
  565. void
  566. setreply(from, when)
  567. char *from;
  568. time_t when;
  569. {
  570. SMDB_DBENT key, data;
  571. memset(&key, '', sizeof key);
  572. memset(&data, '', sizeof data);
  573. key.data.data = from;
  574. key.data.size = strlen(from);
  575. data.data.data = (char*) &when;
  576. data.data.size = sizeof(when);
  577. (void)(Db->smdb_put)(Db, &key, &data, 0);
  578. }
  579. /*
  580. ** XCLUDE --
  581. ** add users to vacation db so they don't get a reply.
  582. **
  583. ** Parameters:
  584. ** f -- file pointer with list of address to exclude
  585. **
  586. ** Returns:
  587. ** nothing.
  588. **
  589. ** Side Effects:
  590. ** stores users in database.
  591. */
  592. void
  593. xclude(f)
  594. FILE *f;
  595. {
  596. char buf[MAXLINE], *p;
  597. if (f == NULL)
  598. return;
  599. while (fgets(buf, sizeof buf, f))
  600. {
  601. if ((p = strchr(buf, 'n')) != NULL)
  602. *p = '';
  603. setreply(buf, ONLY_ONCE);
  604. }
  605. }
  606. /*
  607. ** SENDMESSAGE --
  608. ** exec sendmail to send the vacation file to sender
  609. **
  610. ** Parameters:
  611. ** myname -- user name.
  612. ** msgfn -- name of file with vacation message.
  613. ** emptysender -- use <> as sender address?
  614. **
  615. ** Returns:
  616. ** nothing.
  617. **
  618. ** Side Effects:
  619. ** sends vacation reply.
  620. */
  621. void
  622. sendmessage(myname, msgfn, emptysender)
  623. char *myname;
  624. char *msgfn;
  625. bool emptysender;
  626. {
  627. FILE *mfp, *sfp;
  628. int i;
  629. int pvect[2];
  630. char buf[MAXLINE];
  631. mfp = fopen(msgfn, "r");
  632. if (mfp == NULL)
  633. {
  634. if (msgfn[0] == '/')
  635. syslog(LOG_NOTICE, "vacation: no %s file.n", msgfn);
  636. else
  637. syslog(LOG_NOTICE, "vacation: no ~%s/%s file.n",
  638.        myname, msgfn);
  639. exit(EX_NOINPUT);
  640. }
  641. if (pipe(pvect) < 0)
  642. {
  643. syslog(LOG_ERR, "vacation: pipe: %s", strerror(errno));
  644. exit(EX_OSERR);
  645. }
  646. i = fork();
  647. if (i < 0)
  648. {
  649. syslog(LOG_ERR, "vacation: fork: %s", strerror(errno));
  650. exit(EX_OSERR);
  651. }
  652. if (i == 0)
  653. {
  654. (void) dup2(pvect[0], 0);
  655. (void) close(pvect[0]);
  656. (void) close(pvect[1]);
  657. (void) fclose(mfp);
  658. if (emptysender)
  659. myname = "<>";
  660. (void) execl(_PATH_SENDMAIL, "sendmail", "-f", myname, "--",
  661.       From, NULL);
  662. syslog(LOG_ERR, "vacation: can't exec %s: %s",
  663. _PATH_SENDMAIL, errstring(errno));
  664. exit(EX_UNAVAILABLE);
  665. }
  666. /* check return status of the following calls? XXX */
  667. (void) close(pvect[0]);
  668. if ((sfp = fdopen(pvect[1], "w")) != NULL)
  669. {
  670. (void) fprintf(sfp, "To: %sn", From);
  671. (void) fprintf(sfp, "Auto-Submitted: auto-generatedn");
  672. while (fgets(buf, sizeof buf, mfp))
  673. (void) fputs(buf, sfp);
  674. (void) fclose(mfp);
  675. (void) fclose(sfp);
  676. }
  677. else
  678. {
  679. (void) fclose(mfp);
  680. syslog(LOG_ERR, "vacation: can't open pipe to sendmail");
  681. exit(EX_UNAVAILABLE);
  682. }
  683. }
  684. void
  685. usage()
  686. {
  687. syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] [-f db] [-m msg] [-r interval] [-s sender] [-x] [-z] loginn",
  688.     getuid());
  689. exit(EX_USAGE);
  690. }
  691. /*VARARGS1*/
  692. void
  693. #ifdef __STDC__
  694. message(const char *msg, ...)
  695. #else /* __STDC__ */
  696. message(msg, va_alist)
  697. const char *msg;
  698. va_dcl
  699. #endif /* __STDC__ */
  700. {
  701. const char *m;
  702. VA_LOCAL_DECL
  703. m = msg;
  704. if (isascii(m[0]) && isdigit(m[0]) &&
  705.     isascii(m[1]) && isdigit(m[1]) &&
  706.     isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
  707. m += 4;
  708. VA_START(msg);
  709. (void) vfprintf(stderr, m, ap);
  710. VA_END;
  711. (void) fprintf(stderr, "n");
  712. }
  713. /*VARARGS1*/
  714. void
  715. #ifdef __STDC__
  716. syserr(const char *msg, ...)
  717. #else /* __STDC__ */
  718. syserr(msg, va_alist)
  719. const char *msg;
  720. va_dcl
  721. #endif /* __STDC__ */
  722. {
  723. const char *m;
  724. VA_LOCAL_DECL
  725. m = msg;
  726. if (isascii(m[0]) && isdigit(m[0]) &&
  727.     isascii(m[1]) && isdigit(m[1]) &&
  728.     isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
  729. m += 4;
  730. VA_START(msg);
  731. (void) vfprintf(stderr, m, ap);
  732. VA_END;
  733. (void) fprintf(stderr, "n");
  734. }
  735. void
  736. dumpfd(fd, printclosed, logit)
  737. int fd;
  738. bool printclosed;
  739. bool logit;
  740. {
  741. return;
  742. }