OSDN Git Service

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