OSDN Git Service

update year to 2019
[jnethack/source.git] / src / mail.c
1 /* NetHack 3.6  mail.c  $NHDT-Date: 1519070343 2018/02/19 19:59:03 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.31 $ */
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-2019            */
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*/
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         place_monster(md, fx, fy); /* put md down */
353         newsym(fx, fy);            /* see it */
354         flush_screen(0);           /* make sure md shows up */
355         delay_output();            /* wait a little bit */
356
357         /* Remove md from the dungeon.  Restore original mon, if necessary. */
358         if (mon) {
359             if ((mon->mx != fx) || (mon->my != fy))
360                 place_worm_seg(mon, fx, fy);
361             else
362                 place_monster(mon, fx, fy);
363         } else
364             remove_monster(fx, fy);
365         newsym(fx, fy);
366     }
367
368     /*
369      * Check for a monster at our stopping position (this is possible, but
370      * very unlikely).  If one exists, then have the md leave in disgust.
371      */
372     if ((mon = m_at(fx, fy)) != 0) {
373         place_monster(md, fx, fy); /* display md with text below */
374         newsym(fx, fy);
375 /*JP
376         verbalize("This place's too crowded.  I'm outta here.");
377 */
378         verbalize("\82±\82±\82Í\8d¬\82Ý\82·\82¬\81D\82±\82±\82Å\91Ò\82Á\82Ä\82é\82æ\81D");
379
380         if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */
381             place_worm_seg(mon, fx, fy);
382         else
383             place_monster(mon, fx, fy);
384
385         newsym(fx, fy);
386         return FALSE;
387     }
388
389     place_monster(md, fx, fy); /* place at final spot */
390     newsym(fx, fy);
391     flush_screen(0);
392     delay_output(); /* wait a little bit */
393
394     return TRUE;
395 }
396
397 /* Deliver a scroll of mail. */
398 /*ARGSUSED*/
399 STATIC_OVL void
400 newmail(info)
401 struct mail_info *info;
402 {
403     struct monst *md;
404     coord start, stop;
405     boolean message_seen = FALSE;
406
407     /* Try to find good starting and stopping places. */
408     if (!md_start(&start) || !md_stop(&stop, &start))
409         goto give_up;
410
411     /* Make the daemon.  Have it rush towards the hero. */
412     if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y, NO_MM_FLAGS)))
413         goto give_up;
414     if (!md_rush(md, stop.x, stop.y))
415         goto go_back;
416
417     message_seen = TRUE;
418 #if 0 /*JP*/
419     verbalize("%s, %s!  %s.", Hello(md), plname, info->display_txt);
420 #else
421     verbalize("%s\81I%s\81D", Hello(md), info->display_txt);
422 #endif
423
424     if (info->message_typ) {
425         struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE);
426
427         if (info->object_nam)
428             obj = oname(obj, info->object_nam);
429         if (info->response_cmd)
430             new_omailcmd(obj, info->response_cmd);
431
432         if (distu(md->mx, md->my) > 2)
433 /*JP
434             verbalize("Catch!");
435 */
436             verbalize("\82Ù\82ç\82æ\81I");
437         display_nhwindow(WIN_MESSAGE, FALSE);
438 #if 0 /*JP*/
439         obj = hold_another_object(obj, "Oops!", (const char *) 0,
440                                   (const char *) 0);
441 #else
442         obj = hold_another_object(obj, "\82¨\82Á\82Æ\81I", (const char *) 0,
443                                   (const char *) 0);
444 #endif
445     }
446
447 /* zip back to starting location */
448 go_back:
449     (void) md_rush(md, start.x, start.y);
450     mongone(md);
451 /* deliver some classes of messages even if no daemon ever shows up */
452 give_up:
453     if (!message_seen && info->message_typ == MSG_OTHER)
454 /*JP
455         pline("Hark!  \"%s.\"", info->display_txt);
456 */
457         pline("\81u%s\81D\81v\82Æ\8c¾\82¤\82±\82Æ\82¾\81I", info->display_txt);
458 }
459
460 #if !defined(UNIX) && !defined(VMS)
461
462 void
463 ckmailstatus()
464 {
465     if (u.uswallow || !flags.biff)
466         return;
467     if (mustgetmail < 0) {
468 #if defined(AMIGA) || defined(MSDOS) || defined(TOS)
469         mustgetmail = (moves < 2000) ? (100 + rn2(2000)) : (2000 + rn2(3000));
470 #endif
471         return;
472     }
473     if (--mustgetmail <= 0) {
474         static struct mail_info deliver = {
475 /*JP
476             MSG_MAIL, "I have some mail for you", 0, 0
477 */
478             MSG_MAIL, "\83\81\81[\83\8b\82ð\8e\9d\82Á\82Ä\82«\82½\82æ", 0, 0
479         };
480         newmail(&deliver);
481         mustgetmail = -1;
482     }
483 }
484
485 /*ARGSUSED*/
486 void
487 readmail(otmp)
488 struct obj *otmp UNUSED;
489 {
490     static char *junk[] = {
491         NULL, /* placeholder for "Report bugs to <devteam@nethack.org>.", */
492 /*JP
493         "Please disregard previous letter.", "Welcome to NetHack.",
494 */
495         "\91O\82Ì\83\81\81[\83\8b\82Í\96Y\82ê\82Ä\82­\82¾\82³\82¢\81D", "NetHack\82Ö\82æ\82¤\82±\82»\81I",
496 #ifdef AMIGA
497         "Only Amiga makes it possible.", "CATS have all the answers.",
498 #endif
499 /*JP
500         "This mail complies with the Yendorian Anti-Spam Act (YASA)",
501 */
502         "\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",
503 /*JP
504         "Please find enclosed a small token to represent your Owlbear",
505 */
506         "\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¢",
507 /*JP
508         "**FR33 P0T10N 0F FULL H34L1NG**",
509 */
510         "**\8a®\91S\89ñ\95\9c\82Ì\96ò\83v\83\8c\83[\83\93\83g**",
511 /*JP
512         "Please return to sender (Asmodeus)",
513 */
514         "\91\97\90M\8eÒ(\83A\83X\83\82\83f\83E\83X)\82É\91\97\82è\95Ô\82µ\82Ä\82­\82¾\82³\82¢",
515 /*JP
516       "Buy a potion of gain level for only $19.99! Guaranteed to be blessed!",
517 */
518       "\83\8c\83x\83\8b\83A\83b\83v\82Ì\96ò\82ª\82½\82Á\82½\82Ì1980\89~!\8fj\95\9f\95Û\8fØ!",
519 /*JP
520         "Invitation: Visit the NetHack web site at http://www.nethack.org!"
521 */
522         "\8fµ\91Ò\8fó: NetHack \83E\83F\83u\83T\83C\83g http://www.nethack.org \82É\97\88\82Ä\82Ë!"
523     };
524
525     /* XXX replace with more general substitution code and add local
526      * contact message.  Also use DEVTEAM_URL */
527     if (junk[0] == NULL) {
528 #define BUGS_FORMAT "Report bugs to <%s>."
529         /* +2 from '%s' suffices as substitute for usual +1 for terminator */
530         junk[0] = (char *) alloc(strlen(BUGS_FORMAT) + strlen(DEVTEAM_EMAIL));
531         Sprintf(junk[0], BUGS_FORMAT, DEVTEAM_EMAIL);
532 #undef BUGS_FORMAT
533     }
534     if (Blind) {
535 /*JP
536         pline("Unfortunately you cannot see what it says.");
537 */
538         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");
539     } else
540 /*JP
541         pline("It reads:  \"%s\"", junk[rn2(SIZE(junk))]);
542 */
543         pline("\82»\82ê\82ð\93Ç\82ñ\82¾\81F\"%s\"", junk[rn2(SIZE(junk))]);
544 }
545
546 #endif /* !UNIX && !VMS */
547
548 #ifdef UNIX
549
550 void
551 ckmailstatus()
552 {
553     ck_server_admin_msg();
554
555     if (!mailbox || u.uswallow || !flags.biff
556 #ifdef MAILCKFREQ
557         || moves < laststattime + MAILCKFREQ
558 #endif
559         )
560         return;
561
562     laststattime = moves;
563     if (stat(mailbox, &nmstat)) {
564 #ifdef PERMANENT_MAILBOX
565         pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox);
566         free_maildata();
567 #else
568         nmstat.st_mtime = 0;
569 #endif
570     } else if (nmstat.st_mtime > omstat.st_mtime) {
571         if (nmstat.st_size) {
572             static struct mail_info deliver = {
573 #ifndef NO_MAILREADER
574 /*JP
575                 MSG_MAIL, "I have some mail for you",
576 */
577                 MSG_MAIL, "\83\81\83C\83\8b\82ð\8e\9d\82Á\82Ä\82«\82½\82æ",
578 #else
579                 /* suppress creation and delivery of scroll of mail */
580 /*JP
581                 MSG_OTHER, "You have some mail in the outside world",
582 */
583                 MSG_OTHER, "\8aO\82Ì\90¢\8aE\82©\82ç\82Ì\83\81\81[\83\8b\82¾",
584 #endif
585                 0, 0
586             };
587             newmail(&deliver);
588         }
589         getmailstatus(); /* might be too late ... */
590     }
591 }
592
593 #if defined(SIMPLE_MAIL) || defined(SERVER_ADMIN_MSG)
594 void
595 read_simplemail(mbox, adminmsg)
596 char *mbox;
597 boolean adminmsg;
598 {
599     FILE* mb = fopen(mbox, "r");
600     char curline[128], *msg;
601     boolean seen_one_already = FALSE;
602 #ifdef SIMPLE_MAIL
603     struct flock fl = { 0 };
604 #endif
605     const char *msgfrom = adminmsg
606 #if 0 /*JP*/
607         ? "The voice of %s booms through the caverns:"
608         : "This message is from '%s'.";
609 #else
610         ? "%s\82Ì\90º\82ª\93´\8cA\82É\8b¿\82«\82í\82½\82Á\82½:"
611         : "\82±\82ê\82Í'%s'\82©\82ç\82Ì\83\81\83b\83Z\81[\83W\82¾\81D";
612 #endif
613
614     if (!mb)
615         goto bail;
616
617 #ifdef SIMPLE_MAIL
618     fl.l_type = F_RDLCK;
619     fl.l_whence = SEEK_SET;
620     fl.l_start = 0;
621     fl.l_len = 0;
622     errno = 0;
623 #endif
624
625     /* Allow this call to block. */
626     if (!adminmsg
627 #ifdef SIMPLE_MAIL
628         && fcntl (fileno (mb), F_SETLKW, &fl) == -1
629 #endif
630         )
631         goto bail;
632
633     while (fgets(curline, 128, mb) != NULL) {
634         if (!adminmsg) {
635 #ifdef SIMPLE_MAIL
636             fl.l_type = F_UNLCK;
637             fcntl (fileno(mb), F_UNLCK, &fl);
638 #endif
639 #if 0 /*JP*/
640             pline("There is a%s message on this scroll.",
641                   seen_one_already ? "nother" : "");
642 #else
643             pline("\82±\82Ì\8aª\95¨\82É\82Í%s\83\81\83b\83Z\81[\83W\82ª\82 \82é\81D",
644                   seen_one_already ? "\82Ü\82¾" : "");
645 #endif
646         }
647         msg = strchr(curline, ':');
648
649         if (!msg)
650             goto bail;
651
652         *msg = '\0';
653         msg++;
654         msg[strlen(msg) - 1] = '\0'; /* kill newline */
655
656         pline(msgfrom, curline);
657         if (adminmsg)
658             verbalize(msg);
659         else
660 /*JP
661             pline("It reads: \"%s\".", msg);
662 */
663             pline("\82»\82ê\82ð\93Ç\82ñ\82¾\81F\81u%s\81v", msg);
664
665         seen_one_already = TRUE;
666 #ifdef SIMPLE_MAIL
667         errno = 0;
668         if (!adminmsg) {
669             fl.l_type = F_RDLCK;
670             fcntl(fileno(mb), F_SETLKW, &fl);
671         }
672 #endif
673     }
674
675 #ifdef SIMPLE_MAIL
676     if (!adminmsg) {
677         fl.l_type = F_UNLCK;
678         fcntl(fileno(mb), F_UNLCK, &fl);
679     }
680 #endif
681     fclose(mb);
682     if (adminmsg)
683         display_nhwindow(WIN_MESSAGE, TRUE);
684     else
685         unlink(mailbox);
686     return;
687 bail:
688     /* bail out _professionally_ */
689     if (!adminmsg)
690 /*JP
691         pline("It appears to be all gibberish.");
692 */
693         pline("\82±\82ê\82Í\82Ü\82Á\82½\82­\82¿\82ñ\82Õ\82ñ\82©\82ñ\82Õ\82ñ\82¾\81D");
694 }
695 #endif /* SIMPLE_MAIL */
696
697 void
698 ck_server_admin_msg()
699 {
700 #ifdef SERVER_ADMIN_MSG
701     static struct stat ost,nst;
702     static long lastchk = 0;
703
704     if (moves < lastchk + SERVER_ADMIN_MSG_CKFREQ) return;
705     lastchk = moves;
706
707     if (!stat(SERVER_ADMIN_MSG, &nst)) {
708         if (nst.st_mtime > ost.st_mtime)
709             read_simplemail(SERVER_ADMIN_MSG, TRUE);
710         ost.st_mtime = nst.st_mtime;
711     }
712 #endif /* SERVER_ADMIN_MSG */
713 }
714
715 /*ARGSUSED*/
716 void
717 readmail(otmp)
718 struct obj *otmp UNUSED;
719 {
720 #ifdef DEF_MAILREADER /* This implies that UNIX is defined */
721     register const char *mr = 0;
722 #endif /* DEF_MAILREADER */
723 #ifdef SIMPLE_MAIL
724     read_simplemail(mailbox, FALSE);
725     return;
726 #endif /* SIMPLE_MAIL */
727 #ifdef DEF_MAILREADER /* This implies that UNIX is defined */
728     display_nhwindow(WIN_MESSAGE, FALSE);
729     if (!(mr = nh_getenv("MAILREADER")))
730         mr = DEF_MAILREADER;
731
732     if (child(1)) {
733         (void) execl(mr, mr, (char *) 0);
734         nh_terminate(EXIT_FAILURE);
735     }
736 #else
737 #ifndef AMS /* AMS mailboxes are directories */
738     display_file(mailbox, TRUE);
739 #endif /* AMS */
740 #endif /* DEF_MAILREADER */
741
742     /* get new stat; not entirely correct: there is a small time
743        window where we do not see new mail */
744     getmailstatus();
745 }
746
747 #endif /* UNIX */
748
749 #ifdef VMS
750
751 extern NDECL(struct mail_info *parse_next_broadcast);
752
753 volatile int broadcasts = 0;
754
755 void
756 ckmailstatus()
757 {
758     struct mail_info *brdcst;
759
760     if (u.uswallow || !flags.biff)
761         return;
762
763     while (broadcasts > 0) { /* process all trapped broadcasts [until] */
764         broadcasts--;
765         if ((brdcst = parse_next_broadcast()) != 0) {
766             newmail(brdcst);
767             break; /* only handle one real message at a time */
768         }
769     }
770 }
771
772 void
773 readmail(otmp)
774 struct obj *otmp;
775 {
776 #ifdef SHELL /* can't access mail reader without spawning subprocess */
777     const char *txt, *cmd;
778     char *p, buf[BUFSZ] = DUMMY, qbuf[BUFSZ];
779     int len;
780
781     /* there should be a command in OMAILCMD */
782     if (has_oname(otmp))
783         txt = ONAME(otmp);
784     else
785         txt = "";
786     len = strlen(txt);
787     if (has_omailcmd(otmp))
788         cmd = OMAILCMD(otmp);
789     if (!cmd || !*cmd)
790         cmd = "SPAWN";
791
792     Sprintf(qbuf, "System command (%s)", cmd);
793     getlin(qbuf, buf);
794     if (*buf != '\033') {
795         for (p = eos(buf); p > buf; *p = '\0')
796             if (*--p != ' ')
797                 break; /* strip trailing spaces */
798         if (*buf)
799             cmd = buf; /* use user entered command */
800         if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!"))
801             cmd = (char *) 0; /* interactive escape */
802
803         vms_doshell(cmd, TRUE);
804         (void) sleep(1);
805     }
806 #else
807     nhUse(otmp);
808 #endif /* SHELL */
809 }
810
811 #endif /* VMS */
812 #endif /* MAIL */
813
814 /*mail.c*/