Merge commit 'origin/vendor/PAM_PASSWDQC'
[dragonfly.git] / games / hack / hack.unix.c
1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2 /* hack.unix.c - version 1.0.3 */
3 /* $FreeBSD: src/games/hack/hack.unix.c,v 1.8 1999/11/16 02:57:13 billf Exp $ */
4 /* $DragonFly: src/games/hack/hack.unix.c,v 1.7 2006/08/21 19:45:32 pavalos Exp $ */
5
6 /* This file collects some Unix dependencies; hack.pager.c contains some more */
7
8 /*
9  * The time is used for:
10  *      - seed for random()
11  *      - year on tombstone and yymmdd in record file
12  *      - phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
13  *      - night and midnight (the undead are dangerous at midnight)
14  *      - determination of what files are "very old"
15  */
16
17 #include <errno.h>
18 #include "hack.h"       /* mainly for index() which depends on BSD */
19
20 #include        <sys/types.h>           /* for time_t and stat */
21 #include        <sys/stat.h>
22 #include        <time.h>
23
24 static struct tm        *getlt(void);
25 static bool              veryold(int);
26 #ifdef MAIL
27 static void              newmail(void);
28 static void              mdrush(struct monst *, bool);
29 #endif
30
31 void
32 setrandom(void)
33 {
34         srandomdev();
35 }
36
37 static struct tm *
38 getlt(void)
39 {
40         time_t date;
41
42         time(&date);
43         return(localtime(&date));
44 }
45
46 int
47 getyear(void)
48 {
49         return(1900 + getlt()->tm_year);
50 }
51
52 char *
53 getdate(void)
54 {
55         static char datestr[7];
56         struct tm *lt = getlt();
57
58         snprintf(datestr, sizeof(datestr), "%02d%02d%02d",
59                 lt->tm_year % 100, lt->tm_mon + 1, lt->tm_mday);
60         return(datestr);
61 }
62
63 int
64 phase_of_the_moon(void)                 /* 0-7, with 0: new, 4: full */
65 {                                       /* moon period: 29.5306 days */
66                                         /* year: 365.2422 days */
67         struct tm *lt = getlt();
68         int epact, diy, golden;
69
70         diy = lt->tm_yday;
71         golden = (lt->tm_year % 19) + 1;
72         epact = (11 * golden + 18) % 30;
73         if ((epact == 25 && golden > 11) || epact == 24)
74                 epact++;
75
76         return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
77 }
78
79 bool
80 night(void)
81 {
82         int hour = getlt()->tm_hour;
83
84         return(hour < 6 || hour > 21);
85 }
86
87 bool
88 midnight(void)
89 {
90         return(getlt()->tm_hour == 0);
91 }
92
93 struct stat buf, hbuf;
94
95 void
96 gethdate(const char *name)
97 {
98 /* old version - for people short of space */
99 char *np;
100
101         name = "/usr/games/hide/hack";
102         if(stat(name, &hbuf))
103                 error("Cannot get status of %s.",
104                         (np = rindex(name, '/')) ? np+1 : name);
105 }
106
107 bool
108 uptodate(int fd)
109 {
110         if(fstat(fd, &buf)) {
111                 pline("Cannot get status of saved level? ");
112                 return(0);
113         }
114         if(buf.st_mtime < hbuf.st_mtime) {
115                 pline("Saved level is out of date. ");
116                 return(0);
117         }
118         return(1);
119 }
120
121 /* see whether we should throw away this xlock file */
122 static bool
123 veryold(int fd)
124 {
125         int i;
126         time_t date;
127
128         if(fstat(fd, &buf)) return(0);                  /* cannot get status */
129         if(buf.st_size != sizeof(int)) return(0);       /* not an xlock file */
130         time(&date);
131         if(date - buf.st_mtime < 3L*24L*60L*60L) {      /* recent */
132                 int lockedpid;  /* should be the same size as hackpid */
133
134                 if(read(fd, (char *)&lockedpid, sizeof(lockedpid)) !=
135                         sizeof(lockedpid))
136                         /* strange ... */
137                         return(0);
138
139                 /* From: Rick Adams <seismo!rick> */
140                 /* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */
141                 /* It will do nothing on V7 or 4.1bsd. */
142                 if(!(kill(lockedpid, 0) == -1 && errno == ESRCH))
143                         return(0);
144         }
145         close(fd);
146         for(i = 1; i <= MAXLEVEL; i++) {                /* try to remove all */
147                 glo(i);
148                 unlink(lock);
149         }
150         glo(0);
151         if(unlink(lock)) return(0);                     /* cannot remove it */
152         return(1);                                      /* success! */
153 }
154
155 void
156 getlock(void)
157 {
158         int i = 0, fd;
159
160         fflush(stdout);
161
162         /* we ignore QUIT and INT at this point */
163         if (link(HLOCK, LLOCK) == -1) {
164                 int errnosv = errno;
165
166                 perror(HLOCK);
167                 printf("Cannot link %s to %s\n", LLOCK, HLOCK);
168                 switch(errnosv) {
169                 case ENOENT:
170                     printf("Perhaps there is no (empty) file %s ?\n", HLOCK);
171                     break;
172                 case EACCES:
173                     printf("It seems you don't have write permission here.\n");
174                     break;
175                 case EEXIST:
176                     printf("(Try again or rm %s.)\n", LLOCK);
177                     break;
178                 default:
179                     printf("I don't know what is wrong.");
180                 }
181                 getret();
182                 error("");
183                 /*NOTREACHED*/
184         }
185
186         regularize(lock);
187         glo(0);
188         if(locknum > 25) locknum = 25;
189
190         do {
191                 if(locknum) lock[0] = 'a' + i++;
192
193                 if((fd = open(lock, 0)) == -1) {
194                         if(errno == ENOENT) goto gotlock;    /* no such file */
195                         perror(lock);
196                         unlink(LLOCK);
197                         error("Cannot open %s", lock);
198                 }
199
200                 if(veryold(fd)) /* if true, this closes fd and unlinks lock */
201                         goto gotlock;
202                 close(fd);
203         } while(i < locknum);
204
205         unlink(LLOCK);
206         error(locknum ? "Too many hacks running now."
207                       : "There is a game in progress under your name.");
208 gotlock:
209         fd = creat(lock, FMASK);
210         if(unlink(LLOCK) == -1)
211                 error("Cannot unlink %s.", LLOCK);
212         if(fd == -1) {
213                 error("cannot creat lock file.");
214         } else {
215                 if(write(fd, (char *) &hackpid, sizeof(hackpid))
216                     != sizeof(hackpid)){
217                         error("cannot write lock");
218                 }
219                 if(close(fd) == -1) {
220                         error("cannot close lock");
221                 }
222         }
223 }
224
225 #ifdef MAIL
226
227 /*
228  * Notify user when new mail has arrived. [Idea from Merlyn Leroy, but
229  * I don't know the details of his implementation.]
230  * { Later note: he disliked my calling a general mailreader and felt that
231  *   hack should do the paging itself. But when I get mail, I want to put it
232  *   in some folder, reply, etc. - it would be unreasonable to put all these
233  *   functions in hack. }
234  * The mail daemon '2' is at present not a real monster, but only a visual
235  * effect. Thus, makemon() is superfluous. This might become otherwise,
236  * however. The motion of '2' is less restrained than usual: diagonal moves
237  * from a DOOR are possible. He might also use SDOOR's. Also, '2' is visible
238  * in a ROOM, even when you are Blind.
239  * Its path should be longer when you are Telepat-hic and Blind.
240  *
241  * Interesting side effects:
242  *      - You can get rich by sending yourself a lot of mail and selling
243  *        it to the shopkeeper. Unfortunately mail isn't very valuable.
244  *      - You might die in case '2' comes along at a critical moment during
245  *        a fight and delivers a scroll the weight of which causes you to
246  *        collapse.
247  *
248  * Possible extensions:
249  *      - Open the file MAIL and do fstat instead of stat for efficiency.
250  *        (But sh uses stat, so this cannot be too bad.)
251  *      - Examine the mail and produce a scroll of mail called "From somebody".
252  *      - Invoke MAILREADER in such a way that only this single letter is read.
253  *
254  *      - Make him lose his mail when a Nymph steals the letter.
255  *      - Do something to the text when the scroll is enchanted or cancelled.
256  */
257 #include        "def.mkroom.h"
258 static struct stat omstat,nmstat;
259 static char *mailbox;
260 static long laststattime;
261
262 void
263 getmailstatus(void)
264 {
265         if(!(mailbox = getenv("MAIL")))
266                 return;
267         if(stat(mailbox, &omstat)){
268 #ifdef PERMANENT_MAILBOX
269                 pline("Cannot get status of MAIL=%s .", mailbox);
270                 mailbox = 0;
271 #else
272                 omstat.st_mtime = 0;
273 #endif /* PERMANENT_MAILBOX */
274         }
275 }
276
277 void
278 ckmailstatus(void)
279 {
280         if(!mailbox
281 #ifdef MAILCKFREQ
282                     || moves < laststattime + MAILCKFREQ
283 #endif /* MAILCKFREQ */
284                                                         )
285                 return;
286         laststattime = moves;
287         if(stat(mailbox, &nmstat)){
288 #ifdef PERMANENT_MAILBOX
289                 pline("Cannot get status of MAIL=%s anymore.", mailbox);
290                 mailbox = 0;
291 #else
292                 nmstat.st_mtime = 0;
293 #endif /* PERMANENT_MAILBOX */
294         } else if(nmstat.st_mtime > omstat.st_mtime) {
295                 if(nmstat.st_size)
296                         newmail();
297                 getmailstatus();        /* might be too late ... */
298         }
299 }
300
301 static void
302 newmail(void)
303 {
304         /* produce a scroll of mail */
305         struct obj *obj;
306         struct monst *md;
307         extern struct permonst pm_mail_daemon;
308
309         obj = mksobj(SCR_MAIL);
310         if(md = makemon(&pm_mail_daemon, u.ux, u.uy)) /* always succeeds */
311                 mdrush(md,0);
312
313         pline("\"Hello, %s! I have some mail for you.\"", plname);
314         if(md) {
315                 if(dist(md->mx,md->my) > 2)
316                         pline("\"Catch!\"");
317                 more();
318
319                 /* let him disappear again */
320                 mdrush(md,1);
321                 mondead(md);
322         }
323
324         obj = addinv(obj);
325         identify(obj);          /* set known and do prinv() */
326 }
327
328 /* make md run through the cave */
329 static void
330 mdrush(struct monst *md, bool away)
331 {
332         int uroom = inroom(u.ux, u.uy);
333         if(uroom >= 0) {
334                 int tmp = rooms[uroom].fdoor;
335                 int cnt = rooms[uroom].doorct;
336                 int fx = u.ux, fy = u.uy;
337                 while(cnt--) {
338                         if(dist(fx,fy) < dist(doors[tmp].x, doors[tmp].y)){
339                                 fx = doors[tmp].x;
340                                 fy = doors[tmp].y;
341                         }
342                         tmp++;
343                 }
344                 tmp_at(-1, md->data->mlet);     /* open call */
345                 if(away) {      /* interchange origin and destination */
346                         unpmon(md);
347                         tmp = fx; fx = md->mx; md->mx = tmp;
348                         tmp = fy; fy = md->my; md->my = tmp;
349                 }
350                 while(fx != md->mx || fy != md->my) {
351                         int dx,dy,nfx = fx,nfy = fy,d1,d2;
352
353                         tmp_at(fx,fy);
354                         d1 = DIST(fx,fy,md->mx,md->my);
355                         for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
356                             if(dx || dy) {
357                                 d2 = DIST(fx+dx,fy+dy,md->mx,md->my);
358                                 if(d2 < d1) {
359                                     d1 = d2;
360                                     nfx = fx+dx;
361                                     nfy = fy+dy;
362                                 }
363                             }
364                         if(nfx != fx || nfy != fy) {
365                             fx = nfx;
366                             fy = nfy;
367                         } else {
368                             if(!away) {
369                                 md->mx = fx;
370                                 md->my = fy;
371                             }
372                             break;
373                         }
374                 }
375                 tmp_at(-1,-1);                  /* close call */
376         }
377         if(!away)
378                 pmon(md);
379 }
380
381 void
382 readmail(void)
383 {
384 #ifdef DEF_MAILREADER                   /* This implies that UNIX is defined */
385         char *mr = 0;
386         more();
387         if(!(mr = getenv("MAILREADER")))
388                 mr = DEF_MAILREADER;
389         if(child(1)){
390                 execl(mr, mr, (char *) 0);
391                 exit(1);
392         }
393 #else /* DEF_MAILREADER */
394         page_file(mailbox, FALSE);
395 #endif /* DEF_MAILREADER */
396         /* get new stat; not entirely correct: there is a small time
397            window where we do not see new mail */
398         getmailstatus();
399 }
400 #endif /* MAIL */
401
402 void
403 regularize(char *s)     /* normalize file name - we don't like ..'s or /'s */
404 {
405         char *lp;
406
407         while((lp = index(s, '.')) || (lp = index(s, '/')))
408                 *lp = '_';
409 }