1 /* NetHack 3.6 mail.c $NHDT-Date: 1568508711 2019/09/15 00:51:51 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.40 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #include "hack.h"
7
8 #ifdef MAIL
9 #ifdef SIMPLE_MAIL
10 # include <fcntl.h>
11 # include <errno.h>
12 #endif /* SIMPLE_MAIL */
13 #include "mail.h"
14
15 /*
16 * Notify user when new mail has arrived. Idea by Merlyn Leroy.
17 *
18 * The mail daemon can move with less than usual restraint. It can:
19 * - move diagonally from a door
20 * - use secret and closed doors
21 * - run through a monster ("Gangway!", etc.)
22 * - run over pools & traps
23 *
24 * Possible extensions:
25 * - Open the file MAIL and do fstat instead of stat for efficiency.
26 * (But sh uses stat, so this cannot be too bad.)
27 * - Examine the mail and produce a scroll of mail named "From somebody".
28 * - Invoke MAILREADER in such a way that only this single mail is read.
29 * - Do something to the text when the scroll is enchanted or cancelled.
30 * - Make the daemon always appear at a stairwell, and have it find a
31 * path to the hero.
32 *
33 * Note by Olaf Seibert: On the Amiga, we usually don't get mail. So we go
34 * through most of the effects at 'random' moments.
35 * Note by Paul Winner: The MSDOS port also 'fakes' the mail daemon at
36 * random intervals.
37 */
38
39 STATIC_DCL boolean FDECL(md_start, (coord *));
40 STATIC_DCL boolean FDECL(md_stop, (coord *, coord *));
41 STATIC_DCL boolean FDECL(md_rush, (struct monst *, int, int));
42 STATIC_DCL void FDECL(newmail, (struct mail_info *));
43
44 extern char *viz_rmin, *viz_rmax; /* line-of-sight limits (vision.c) */
45
46 #if !defined(UNIX) && !defined(VMS)
47 int mustgetmail = -1;
48 #endif
49
50 #ifdef UNIX
51 #include <sys/stat.h>
52 #include <pwd.h>
53 /* DON'T trust all Unices to declare getpwuid() in <pwd.h> */
54 #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
55 #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
56 /* DO trust all SVR4 to typedef uid_t in <sys/types.h> (probably to a long) */
57 #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
58 extern struct passwd *FDECL(getpwuid, (uid_t));
59 #else
60 extern struct passwd *FDECL(getpwuid, (int));
61 #endif
62 #endif
63 #endif
64 static struct stat omstat, nmstat;
65 static char *mailbox = (char *) 0;
66 static long laststattime;
67
68 #if !defined(MAILPATH) && defined(AMS) /* Just a placeholder for AMS */
69 #define MAILPATH "/dev/null"
70 #endif
71 #if !defined(MAILPATH) && (defined(LINUX) || defined(__osf__))
72 #define MAILPATH "/var/spool/mail/"
73 #endif
74 #if !defined(MAILPATH) && defined(__FreeBSD__)
75 #define MAILPATH "/var/mail/"
76 #endif
77 #if !defined(MAILPATH) && (defined(BSD) || defined(ULTRIX))
78 #define MAILPATH "/usr/spool/mail/"
79 #endif
80 #if !defined(MAILPATH) && (defined(SYSV) || defined(HPUX))
81 #define MAILPATH "/usr/mail/"
82 #endif
83
84 void
free_maildata()85 free_maildata()
86 {
87 if (mailbox)
88 free((genericptr_t) mailbox), mailbox = (char *) 0;
89 }
90
91 void
getmailstatus()92 getmailstatus()
93 {
94 if (mailbox) {
95 ; /* no need to repeat the setup */
96 } else if ((mailbox = nh_getenv("MAIL")) != 0) {
97 mailbox = dupstr(mailbox);
98 #ifdef MAILPATH
99 } else {
100 #ifdef AMS
101 struct passwd ppasswd;
102
103 (void) memcpy(&ppasswd, getpwuid(getuid()), sizeof (struct passwd));
104 if (ppasswd.pw_dir) {
105 /* note: 'sizeof "LITERAL"' includes +1 for terminating '\0' */
106 mailbox = (char *) alloc((unsigned) (strlen(ppasswd.pw_dir)
107 + sizeof AMS_MAILBOX));
108 Strcpy(mailbox, ppasswd.pw_dir);
109 Strcat(mailbox, AMS_MAILBOX);
110 }
111 #else
112 const char *pw_name = getpwuid(getuid())->pw_name;
113
114 /* note: 'sizeof "LITERAL"' includes +1 for terminating '\0' */
115 mailbox = (char *) alloc((unsigned) (strlen(pw_name)
116 + sizeof MAILPATH));
117 Strcpy(mailbox, MAILPATH);
118 Strcat(mailbox, pw_name);
119 #endif /* AMS */
120 #endif /* MAILPATH */
121 }
122
123 debugpline3("mailbox=%c%s%c",
124 mailbox ? '\"' : '<',
125 mailbox ? mailbox : "null",
126 mailbox ? '\"' : '>');
127
128 if (mailbox && stat(mailbox, &omstat)) {
129 #ifdef PERMANENT_MAILBOX
130 pline("Cannot get status of MAIL=\"%s\".", mailbox);
131 free_maildata(); /* set 'mailbox' to Null */
132 #else
133 omstat.st_mtime = 0;
134 #endif
135 }
136 }
137 #endif /* UNIX */
138
139 /*
140 * Pick coordinates for a starting position for the mail daemon. Called
141 * from newmail() and newphone().
142 */
143 STATIC_OVL boolean
md_start(startp)144 md_start(startp)
145 coord *startp;
146 {
147 coord testcc; /* scratch coordinates */
148 int row; /* current row we are checking */
149 int lax; /* if TRUE, pick a position in sight. */
150 int dd; /* distance to current point */
151 int max_distance; /* max distance found so far */
152
153 /*
154 * If blind and not telepathic, then it doesn't matter what we pick ---
155 * the hero is not going to see it anyway. So pick a nearby position.
156 */
157 if (Blind && !Blind_telepat) {
158 if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0))
159 return FALSE; /* no good positions */
160 return TRUE;
161 }
162
163 /*
164 * Arrive at an up or down stairwell if it is in line of sight from the
165 * hero.
166 */
167 if (couldsee(upstair.sx, upstair.sy)) {
168 startp->x = upstair.sx;
169 startp->y = upstair.sy;
170 return TRUE;
171 }
172 if (couldsee(dnstair.sx, dnstair.sy)) {
173 startp->x = dnstair.sx;
174 startp->y = dnstair.sy;
175 return TRUE;
176 }
177
178 /*
179 * Try to pick a location out of sight next to the farthest position away
180 * from the hero. If this fails, try again, just picking the farthest
181 * position that could be seen. What we really ought to be doing is
182 * finding a path from a stairwell...
183 *
184 * The arrays viz_rmin[] and viz_rmax[] are set even when blind. These
185 * are the LOS limits for each row.
186 */
187 lax = 0; /* be picky */
188 max_distance = -1;
189 retry:
190 for (row = 0; row < ROWNO; row++) {
191 if (viz_rmin[row] < viz_rmax[row]) {
192 /* There are valid positions on this row. */
193 dd = distu(viz_rmin[row], row);
194 if (dd > max_distance) {
195 if (lax) {
196 max_distance = dd;
197 startp->y = row;
198 startp->x = viz_rmin[row];
199
200 } else if (enexto(&testcc, (xchar) viz_rmin[row], row,
201 (struct permonst *) 0)
202 && !cansee(testcc.x, testcc.y)
203 && couldsee(testcc.x, testcc.y)) {
204 max_distance = dd;
205 *startp = testcc;
206 }
207 }
208 dd = distu(viz_rmax[row], row);
209 if (dd > max_distance) {
210 if (lax) {
211 max_distance = dd;
212 startp->y = row;
213 startp->x = viz_rmax[row];
214
215 } else if (enexto(&testcc, (xchar) viz_rmax[row], row,
216 (struct permonst *) 0)
217 && !cansee(testcc.x, testcc.y)
218 && couldsee(testcc.x, testcc.y)) {
219 max_distance = dd;
220 *startp = testcc;
221 }
222 }
223 }
224 }
225
226 if (max_distance < 0) {
227 if (!lax) {
228 lax = 1; /* just find a position */
229 goto retry;
230 }
231 return FALSE;
232 }
233
234 return TRUE;
235 }
236
237 /*
238 * Try to choose a stopping point as near as possible to the starting
239 * position while still adjacent to the hero. If all else fails, try
240 * enexto(). Use enexto() as a last resort because enexto() chooses
241 * its point randomly, which is not what we want.
242 */
243 STATIC_OVL boolean
md_stop(stopp,startp)244 md_stop(stopp, startp)
245 coord *stopp; /* stopping position (we fill it in) */
246 coord *startp; /* starting position (read only) */
247 {
248 int x, y, distance, min_distance = -1;
249
250 for (x = u.ux - 1; x <= u.ux + 1; x++)
251 for (y = u.uy - 1; y <= u.uy + 1; y++) {
252 if (!isok(x, y) || (x == u.ux && y == u.uy))
253 continue;
254
255 if (accessible(x, y) && !MON_AT(x, y)) {
256 distance = dist2(x, y, startp->x, startp->y);
257 if (min_distance < 0 || distance < min_distance
258 || (distance == min_distance && rn2(2))) {
259 stopp->x = x;
260 stopp->y = y;
261 min_distance = distance;
262 }
263 }
264 }
265
266 /* If we didn't find a good spot, try enexto(). */
267 if (min_distance < 0 && !enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON]))
268 return FALSE;
269
270 return TRUE;
271 }
272
273 /* Let the mail daemon have a larger vocabulary. */
274 static NEARDATA const char *mail_text[] = { "Gangway!", "Look out!",
275 "Pardon me!" };
276 #define md_exclamations() (mail_text[rn2(3)])
277
278 /*
279 * Make the mail daemon run through the dungeon. The daemon will run over
280 * any monsters that are in its path, but will replace them later. Return
281 * FALSE if the md gets stuck in a position where there is a monster. Return
282 * TRUE otherwise.
283 */
284 STATIC_OVL boolean
md_rush(md,tx,ty)285 md_rush(md, tx, ty)
286 struct monst *md;
287 register int tx, ty; /* destination of mail daemon */
288 {
289 struct monst *mon; /* displaced monster */
290 register int dx, dy; /* direction counters */
291 int fx = md->mx, fy = md->my; /* current location */
292 int nfx = fx, nfy = fy, /* new location */
293 d1, d2; /* shortest distances */
294
295 /*
296 * It is possible that the monster at (fx,fy) is not the md when:
297 * the md rushed the hero and failed, and is now starting back.
298 */
299 if (m_at(fx, fy) == md) {
300 remove_monster(fx, fy); /* pick up from orig position */
301 newsym(fx, fy);
302 }
303
304 /*
305 * At the beginning and exit of this loop, md is not placed in the
306 * dungeon.
307 */
308 while (1) {
309 /* Find a good location next to (fx,fy) closest to (tx,ty). */
310 d1 = dist2(fx, fy, tx, ty);
311 for (dx = -1; dx <= 1; dx++)
312 for (dy = -1; dy <= 1; dy++)
313 if ((dx || dy) && isok(fx + dx, fy + dy)
314 && !IS_STWALL(levl[fx + dx][fy + dy].typ)) {
315 d2 = dist2(fx + dx, fy + dy, tx, ty);
316 if (d2 < d1) {
317 d1 = d2;
318 nfx = fx + dx;
319 nfy = fy + dy;
320 }
321 }
322
323 /* Break if the md couldn't find a new position. */
324 if (nfx == fx && nfy == fy)
325 break;
326
327 fx = nfx; /* this is our new position */
328 fy = nfy;
329
330 /* Break if the md reaches its destination. */
331 if (fx == tx && fy == ty)
332 break;
333
334 if ((mon = m_at(fx, fy)) != 0) /* save monster at this position */
335 verbalize1(md_exclamations());
336 else if (fx == u.ux && fy == u.uy)
337 verbalize("Excuse me.");
338
339 if (mon)
340 remove_monster(fx, fy);
341 place_monster(md, fx, fy); /* put md down */
342 newsym(fx, fy); /* see it */
343 flush_screen(0); /* make sure md shows up */
344 delay_output(); /* wait a little bit */
345
346 /* Remove md from the dungeon. Restore original mon, if necessary. */
347 remove_monster(fx, fy);
348 if (mon) {
349 if ((mon->mx != fx) || (mon->my != fy))
350 place_worm_seg(mon, fx, fy);
351 else
352 place_monster(mon, fx, fy);
353 }
354 newsym(fx, fy);
355 }
356
357 /*
358 * Check for a monster at our stopping position (this is possible, but
359 * very unlikely). If one exists, then have the md leave in disgust.
360 */
361 if ((mon = m_at(fx, fy)) != 0) {
362 remove_monster(fx, fy);
363 place_monster(md, fx, fy); /* display md with text below */
364 newsym(fx, fy);
365 verbalize("This place's too crowded. I'm outta here.");
366 remove_monster(fx, fy);
367
368 if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */
369 place_worm_seg(mon, fx, fy);
370 else
371 place_monster(mon, fx, fy);
372
373 newsym(fx, fy);
374 return FALSE;
375 }
376
377 place_monster(md, fx, fy); /* place at final spot */
378 newsym(fx, fy);
379 flush_screen(0);
380 delay_output(); /* wait a little bit */
381
382 return TRUE;
383 }
384
385 /* Deliver a scroll of mail. */
386 /*ARGSUSED*/
387 STATIC_OVL void
newmail(info)388 newmail(info)
389 struct mail_info *info;
390 {
391 struct monst *md;
392 coord start, stop;
393 boolean message_seen = FALSE;
394
395 /* Try to find good starting and stopping places. */
396 if (!md_start(&start) || !md_stop(&stop, &start))
397 goto give_up;
398
399 /* Make the daemon. Have it rush towards the hero. */
400 if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y, NO_MM_FLAGS)))
401 goto give_up;
402 if (!md_rush(md, stop.x, stop.y))
403 goto go_back;
404
405 message_seen = TRUE;
406 verbalize("%s, %s! %s.", Hello(md), plname, info->display_txt);
407
408 if (info->message_typ) {
409 struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE);
410
411 if (info->object_nam)
412 obj = oname(obj, info->object_nam);
413 if (info->response_cmd)
414 new_omailcmd(obj, info->response_cmd);
415
416 if (distu(md->mx, md->my) > 2)
417 verbalize("Catch!");
418 display_nhwindow(WIN_MESSAGE, FALSE);
419 obj = hold_another_object(obj, "Oops!", (const char *) 0,
420 (const char *) 0);
421 nhUse(obj);
422 }
423
424 go_back:
425 /* zip back to starting location */
426 if (!md_rush(md, start.x, start.y))
427 md->mx = md->my = 0; /* for mongone, md is not on map */
428 mongone(md);
429
430 give_up:
431 /* deliver some classes of messages even if no daemon ever shows up */
432 if (!message_seen && info->message_typ == MSG_OTHER)
433 pline("Hark! \"%s.\"", info->display_txt);
434 }
435
436 #if !defined(UNIX) && !defined(VMS)
437
438 void
ckmailstatus()439 ckmailstatus()
440 {
441 if (u.uswallow || !flags.biff)
442 return;
443 if (mustgetmail < 0) {
444 #if defined(AMIGA) || defined(MSDOS) || defined(TOS)
445 mustgetmail = (moves < 2000) ? (100 + rn2(2000)) : (2000 + rn2(3000));
446 #endif
447 return;
448 }
449 if (--mustgetmail <= 0) {
450 static struct mail_info deliver = {
451 MSG_MAIL, "I have some mail for you", 0, 0
452 };
453 newmail(&deliver);
454 mustgetmail = -1;
455 }
456 }
457
458 /*ARGSUSED*/
459 void
readmail(otmp)460 readmail(otmp)
461 struct obj *otmp UNUSED;
462 {
463 static const char *junk[] = {
464 "Report bugs to <%s>.", /*** must be first entry ***/
465 "Please disregard previous letter.",
466 "Welcome to NetHack.",
467 #ifdef AMIGA
468 "Only Amiga makes it possible.",
469 "CATS have all the answers.",
470 #endif
471 "This mail complies with the Yendorian Anti-Spam Act (YASA)",
472 "Please find enclosed a small token to represent your Owlbear",
473 "**FR33 P0T10N 0F FULL H34L1NG**",
474 "Please return to sender (Asmodeus)",
475 /* when enclosed by "It reads: \"...\"", this is too long
476 for an ordinary 80-column display so wraps to a second line
477 (suboptimal but works correctly);
478 dollar sign and fractional zorkmids are inappropriate within
479 nethack but are suitable for typical dysfunctional spam mail */
480 "Buy a potion of gain level for only $19.99! Guaranteed to be blessed!",
481 /* DEVTEAM_URL will be substituted for "%s"; terminating punctuation
482 (formerly "!") has deliberately been omitted so that it can't be
483 mistaken for part of the URL (unfortunately that is still followed
484 by a closing quote--in the pline below, not the data here) */
485 "Invitation: Visit the NetHack web site at %s"
486 };
487
488 /* XXX replace with more general substitution code and add local
489 * contact message.
490 *
491 * FIXME: this allocated memory is never freed. However, if the
492 * game is restarted, the junk[] update will be a no-op for second
493 * and subsequent runs and this updated text will still be appropriate.
494 */
495 if (index(junk[0], '%')) {
496 char *tmp;
497 int i;
498
499 for (i = 0; i < SIZE(junk); ++i) {
500 if (index(junk[i], '%')) {
501 if (i == 0) {
502 /* +2 from '%s' in junk[0] suffices as substitute
503 for usual +1 for terminator */
504 tmp = (char *) alloc(strlen(junk[0])
505 + strlen(DEVTEAM_EMAIL));
506 Sprintf(tmp, junk[0], DEVTEAM_EMAIL);
507 junk[0] = tmp;
508 } else if (strstri(junk[i], "web site")) {
509 /* as with junk[0], room for terminator is present */
510 tmp = (char *) alloc(strlen(junk[i])
511 + strlen(DEVTEAM_URL));
512 Sprintf(tmp, junk[i], DEVTEAM_URL);
513 junk[i] = tmp;
514 } else {
515 /* could check for "%%" but unless that becomes needed,
516 handling it is more complicated than necessary */
517 impossible("fake mail #%d has undefined substitution", i);
518 junk[i] = "Bad fake mail...";
519 }
520 }
521 }
522 }
523 if (Blind) {
524 pline("Unfortunately you cannot see what it says.");
525 } else
526 pline("It reads: \"%s\"", junk[rn2(SIZE(junk))]);
527 }
528
529 #endif /* !UNIX && !VMS */
530
531 #ifdef UNIX
532
533 void
ckmailstatus()534 ckmailstatus()
535 {
536 ck_server_admin_msg();
537
538 if (!mailbox || u.uswallow || !flags.biff
539 #ifdef MAILCKFREQ
540 || moves < laststattime + MAILCKFREQ
541 #endif
542 )
543 return;
544
545 laststattime = moves;
546 if (stat(mailbox, &nmstat)) {
547 #ifdef PERMANENT_MAILBOX
548 pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox);
549 free_maildata();
550 #else
551 nmstat.st_mtime = 0;
552 #endif
553 } else if (nmstat.st_mtime > omstat.st_mtime) {
554 if (nmstat.st_size) {
555 static struct mail_info deliver = {
556 #ifndef NO_MAILREADER
557 MSG_MAIL, "I have some mail for you",
558 #else
559 /* suppress creation and delivery of scroll of mail */
560 MSG_OTHER, "You have some mail in the outside world",
561 #endif
562 0, 0
563 };
564 newmail(&deliver);
565 }
566 getmailstatus(); /* might be too late ... */
567 }
568 }
569
570 #if defined(SIMPLE_MAIL) || defined(SERVER_ADMIN_MSG)
571 void
read_simplemail(mbox,adminmsg)572 read_simplemail(mbox, adminmsg)
573 char *mbox;
574 boolean adminmsg;
575 {
576 FILE* mb = fopen(mbox, "r");
577 char curline[128], *msg;
578 boolean seen_one_already = FALSE;
579 #ifdef SIMPLE_MAIL
580 struct flock fl = { 0 };
581 #endif
582 const char *msgfrom = adminmsg
583 ? "The voice of %s booms through the caverns:"
584 : "This message is from '%s'.";
585
586 if (!mb)
587 goto bail;
588
589 #ifdef SIMPLE_MAIL
590 fl.l_type = F_RDLCK;
591 fl.l_whence = SEEK_SET;
592 fl.l_start = 0;
593 fl.l_len = 0;
594 errno = 0;
595 #endif
596
597 /* Allow this call to block. */
598 if (!adminmsg
599 #ifdef SIMPLE_MAIL
600 && fcntl (fileno (mb), F_SETLKW, &fl) == -1
601 #endif
602 )
603 goto bail;
604
605 while (fgets(curline, 128, mb) != NULL) {
606 if (!adminmsg) {
607 #ifdef SIMPLE_MAIL
608 fl.l_type = F_UNLCK;
609 fcntl (fileno(mb), F_UNLCK, &fl);
610 #endif
611 pline("There is a%s message on this scroll.",
612 seen_one_already ? "nother" : "");
613 }
614 msg = strchr(curline, ':');
615
616 if (!msg)
617 goto bail;
618
619 *msg = '\0';
620 msg++;
621 msg[strlen(msg) - 1] = '\0'; /* kill newline */
622
623 pline(msgfrom, curline);
624 if (adminmsg)
625 verbalize(msg);
626 else
627 pline("It reads: \"%s\".", msg);
628
629 seen_one_already = TRUE;
630 #ifdef SIMPLE_MAIL
631 errno = 0;
632 if (!adminmsg) {
633 fl.l_type = F_RDLCK;
634 fcntl(fileno(mb), F_SETLKW, &fl);
635 }
636 #endif
637 }
638
639 #ifdef SIMPLE_MAIL
640 if (!adminmsg) {
641 fl.l_type = F_UNLCK;
642 fcntl(fileno(mb), F_UNLCK, &fl);
643 }
644 #endif
645 fclose(mb);
646 if (adminmsg)
647 display_nhwindow(WIN_MESSAGE, TRUE);
648 else
649 unlink(mailbox);
650 return;
651 bail:
652 /* bail out _professionally_ */
653 if (!adminmsg)
654 pline("It appears to be all gibberish.");
655 }
656 #endif /* SIMPLE_MAIL */
657
658 void
ck_server_admin_msg()659 ck_server_admin_msg()
660 {
661 #ifdef SERVER_ADMIN_MSG
662 static struct stat ost,nst;
663 static long lastchk = 0;
664
665 if (moves < lastchk + SERVER_ADMIN_MSG_CKFREQ) return;
666 lastchk = moves;
667
668 if (!stat(SERVER_ADMIN_MSG, &nst)) {
669 if (nst.st_mtime > ost.st_mtime)
670 read_simplemail(SERVER_ADMIN_MSG, TRUE);
671 ost.st_mtime = nst.st_mtime;
672 }
673 #endif /* SERVER_ADMIN_MSG */
674 }
675
676 /*ARGSUSED*/
677 void
readmail(otmp)678 readmail(otmp)
679 struct obj *otmp UNUSED;
680 {
681 #ifdef DEF_MAILREADER /* This implies that UNIX is defined */
682 register const char *mr = 0;
683 #endif /* DEF_MAILREADER */
684 #ifdef SIMPLE_MAIL
685 read_simplemail(mailbox, FALSE);
686 return;
687 #endif /* SIMPLE_MAIL */
688 #ifdef DEF_MAILREADER /* This implies that UNIX is defined */
689 if (iflags.debug_fuzzer)
690 return;
691 display_nhwindow(WIN_MESSAGE, FALSE);
692 if (!(mr = nh_getenv("MAILREADER")))
693 mr = DEF_MAILREADER;
694
695 if (child(1)) {
696 (void) execl(mr, mr, (char *) 0);
697 nh_terminate(EXIT_FAILURE);
698 }
699 #else
700 #ifndef AMS /* AMS mailboxes are directories */
701 display_file(mailbox, TRUE);
702 #endif /* AMS */
703 #endif /* DEF_MAILREADER */
704
705 /* get new stat; not entirely correct: there is a small time
706 window where we do not see new mail */
707 getmailstatus();
708 }
709
710 #endif /* UNIX */
711
712 #ifdef VMS
713
714 extern NDECL(struct mail_info *parse_next_broadcast);
715
716 volatile int broadcasts = 0;
717
718 void
ckmailstatus()719 ckmailstatus()
720 {
721 struct mail_info *brdcst;
722
723 if (iflags.debug_fuzzer)
724 return;
725 if (u.uswallow || !flags.biff)
726 return;
727
728 while (broadcasts > 0) { /* process all trapped broadcasts [until] */
729 broadcasts--;
730 if ((brdcst = parse_next_broadcast()) != 0) {
731 newmail(brdcst);
732 break; /* only handle one real message at a time */
733 }
734 }
735 }
736
737 void
readmail(otmp)738 readmail(otmp)
739 struct obj *otmp;
740 {
741 #ifdef SHELL /* can't access mail reader without spawning subprocess */
742 const char *txt, *cmd;
743 char *p, buf[BUFSZ] = DUMMY, qbuf[BUFSZ];
744 int len;
745
746 /* there should be a command in OMAILCMD */
747 if (has_oname(otmp))
748 txt = ONAME(otmp);
749 else
750 txt = "";
751 len = strlen(txt);
752 if (has_omailcmd(otmp))
753 cmd = OMAILCMD(otmp);
754 if (!cmd || !*cmd)
755 cmd = "SPAWN";
756
757 Sprintf(qbuf, "System command (%s)", cmd);
758 getlin(qbuf, buf);
759 if (*buf != '\033') {
760 for (p = eos(buf); p > buf; *p = '\0')
761 if (*--p != ' ')
762 break; /* strip trailing spaces */
763 if (*buf)
764 cmd = buf; /* use user entered command */
765 if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!"))
766 cmd = (char *) 0; /* interactive escape */
767
768 vms_doshell(cmd, TRUE);
769 (void) sleep(1);
770 }
771 #else
772 nhUse(otmp);
773 #endif /* SHELL */
774 }
775
776 #endif /* VMS */
777 #endif /* MAIL */
778
779 /*mail.c*/
780