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