1 /* $OpenBSD: hack.unix.c,v 1.23 2023/09/06 11:53:56 jsg Exp $ */
2
3 /*
4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5 * Amsterdam
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * - Neither the name of the Stichting Centrum voor Wiskunde en
20 * Informatica, nor the names of its contributors may be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 /*
38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39 * All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 * 3. The name of the author may not be used to endorse or promote products
50 * derived from this software without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 */
63
64 /* This file collects some Unix dependencies; hack.pager.c contains some more */
65
66 /*
67 * The time is used for:
68 * - seed for random()
69 * - year on tombstone and yymmdd in record file
70 * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
71 * - night and midnight (the undead are dangerous at midnight)
72 * - determination of what files are "very old"
73 */
74
75 #include <sys/stat.h>
76
77 #include <err.h>
78 #include <errno.h>
79 #include <limits.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <time.h>
84 #include <unistd.h>
85
86 #include "hack.h"
87
88
89 static struct tm *getlt(void);
90 static int veryold(int);
91 #ifdef MAIL
92 static void newmail(void);
93 static void mdrush(struct monst *, boolean);
94 #endif
95
96 static struct tm *
getlt(void)97 getlt(void)
98 {
99 time_t date;
100
101 (void) time(&date);
102 return(localtime(&date));
103 }
104
105 int
getyear(void)106 getyear(void)
107 {
108 return(1900 + getlt()->tm_year);
109 }
110
111 char *
getdate(void)112 getdate(void)
113 {
114 static char datestr[7];
115 struct tm *lt = getlt();
116
117 (void) snprintf(datestr, sizeof(datestr), "%02d%02d%02d",
118 lt->tm_year % 100, lt->tm_mon + 1, lt->tm_mday);
119 return(datestr);
120 }
121
122 /*
123 * 0-7, with 0: new, 4: full
124 * moon period: 29.5306 days
125 * year: 365.2422 days
126 */
127 int
phase_of_the_moon(void)128 phase_of_the_moon(void)
129 {
130 struct tm *lt = getlt();
131 int epact, diy, golden;
132
133 diy = lt->tm_yday;
134 golden = (lt->tm_year % 19) + 1;
135 epact = (11 * golden + 18) % 30;
136 if ((epact == 25 && golden > 11) || epact == 24)
137 epact++;
138
139 return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
140 }
141
142 int
night(void)143 night(void)
144 {
145 int hour = getlt()->tm_hour;
146
147 return(hour < 6 || hour > 21);
148 }
149
150 int
midnight(void)151 midnight(void)
152 {
153 return(getlt()->tm_hour == 0);
154 }
155
156 struct stat buf;
157
158 /* see whether we should throw away this xlock file */
159 static int
veryold(int fd)160 veryold(int fd)
161 {
162 int i;
163 time_t date;
164
165 if(fstat(fd, &buf)) return(0); /* cannot get status */
166 if(buf.st_size != sizeof(int)) return(0); /* not an xlock file */
167 (void) time(&date);
168 if(date - buf.st_mtime < 3L*24L*60L*60L) { /* recent */
169 int lockedpid; /* should be the same size as hackpid */
170
171 if(read(fd, (char *)&lockedpid, sizeof(lockedpid)) !=
172 sizeof(lockedpid))
173 /* strange ... */
174 return(0);
175
176 /* From: Rick Adams <seismo!rick>
177 This will work on 4.1cbsd, 4.2bsd and system 3? & 5.
178 It will do nothing on V7 or 4.1bsd. */
179 if(!(kill(lockedpid, 0) == -1 && errno == ESRCH))
180 return(0);
181 }
182 (void) close(fd);
183 for(i = 1; i <= MAXLEVEL; i++) { /* try to remove all */
184 glo(i);
185 (void) unlink(lock);
186 }
187 glo(0);
188 if(unlink(lock)) return(0); /* cannot remove it */
189 return(1); /* success! */
190 }
191
192 void
getlock(void)193 getlock(void)
194 {
195 extern int hackpid, locknum;
196 int i = 0, fd;
197
198 (void) fflush(stdout);
199
200 /* we ignore QUIT and INT at this point */
201 if (link(HLOCK, LLOCK) == -1) {
202 int errnosv = errno;
203
204 perror(HLOCK);
205 printf("Cannot link %s to %s\n", LLOCK, HLOCK);
206 switch(errnosv) {
207 case ENOENT:
208 printf("Perhaps there is no (empty) file %s ?\n", HLOCK);
209 break;
210 case EACCES:
211 printf("It seems you don't have write permission here.\n");
212 break;
213 case EEXIST:
214 printf("(Try again or rm %s.)\n", LLOCK);
215 break;
216 default:
217 printf("I don't know what is wrong.");
218 }
219 getret();
220 error("");
221 }
222
223 regularize(lock);
224 glo(0);
225 if(locknum > 25) locknum = 25;
226
227 do {
228 if(locknum) lock[0] = 'a' + i++;
229
230 if((fd = open(lock, O_RDONLY)) == -1) {
231 if(errno == ENOENT) goto gotlock; /* no such file */
232 perror(lock);
233 (void) unlink(LLOCK);
234 error("Cannot open %s", lock);
235 }
236
237 if(veryold(fd)) /* if true, this closes fd and unlinks lock */
238 goto gotlock;
239 (void) close(fd);
240 } while(i < locknum);
241
242 (void) unlink(LLOCK);
243 error(locknum ? "Too many hacks running now."
244 : "There is a game in progress under your name.");
245 gotlock:
246 fd = open(lock, O_CREAT | O_TRUNC | O_WRONLY, FMASK);
247 if(unlink(LLOCK) == -1)
248 error("Cannot unlink %s.", LLOCK);
249 if(fd == -1) {
250 error("cannot creat lock file.");
251 } else {
252 if(write(fd, (char *) &hackpid, sizeof(hackpid))
253 != sizeof(hackpid)){
254 error("cannot write lock");
255 }
256 if(close(fd) == -1) {
257 error("cannot close lock");
258 }
259 }
260 }
261
262 #ifdef MAIL
263
264 /*
265 * Notify user when new mail has arrived. [Idea from Merlyn Leroy, but
266 * I don't know the details of his implementation.]
267 * { Later note: he disliked my calling a general mailreader and felt that
268 * hack should do the paging itself. But when I get mail, I want to put it
269 * in some folder, reply, etc. - it would be unreasonable to put all these
270 * functions in hack. }
271 * The mail daemon '2' is at present not a real monster, but only a visual
272 * effect. Thus, makemon() is superfluous. This might become otherwise,
273 * however. The motion of '2' is less restrained than usual: diagonal moves
274 * from a DOOR are possible. He might also use SDOOR's. Also, '2' is visible
275 * in a ROOM, even when you are Blind.
276 * Its path should be longer when you are Telepat-hic and Blind.
277 *
278 * Interesting side effects:
279 * - You can get rich by sending yourself a lot of mail and selling
280 * it to the shopkeeper. Unfortunately mail isn't very valuable.
281 * - You might die in case '2' comes along at a critical moment during
282 * a fight and delivers a scroll the weight of which causes you to
283 * collapse.
284 *
285 * Possible extensions:
286 * - Open the file MAIL and do fstat instead of stat for efficiency.
287 * (But sh uses stat, so this cannot be too bad.)
288 * - Examine the mail and produce a scroll of mail called "From somebody".
289 * - Invoke MAILREADER in such a way that only this single letter is read.
290 *
291 * - Make him lose his mail when a Nymph steals the letter.
292 * - Do something to the text when the scroll is enchanted or cancelled.
293 */
294 static struct stat omstat,nmstat;
295 static char *mailbox;
296 static long laststattime;
297
298 void
getmailstatus(void)299 getmailstatus(void)
300 {
301 if(!(mailbox = getenv("MAIL")))
302 return;
303 if(stat(mailbox, &omstat)){
304 #ifdef PERMANENT_MAILBOX
305 pline("Cannot get status of MAIL=%s .", mailbox);
306 mailbox = 0;
307 #else
308 omstat.st_mtime = 0;
309 #endif /* PERMANENT_MAILBOX */
310 }
311 }
312
313 void
ckmailstatus(void)314 ckmailstatus(void)
315 {
316 if(!mailbox
317 #ifdef MAILCKFREQ
318 || moves < laststattime + MAILCKFREQ
319 #endif /* MAILCKFREQ */
320 )
321 return;
322 laststattime = moves;
323 if(stat(mailbox, &nmstat)){
324 #ifdef PERMANENT_MAILBOX
325 pline("Cannot get status of MAIL=%s anymore.", mailbox);
326 mailbox = 0;
327 #else
328 nmstat.st_mtime = 0;
329 #endif /* PERMANENT_MAILBOX */
330 } else if(nmstat.st_mtime > omstat.st_mtime) {
331 if(nmstat.st_size)
332 newmail();
333 getmailstatus(); /* might be too late ... */
334 }
335 }
336
337 static void
newmail(void)338 newmail(void)
339 {
340 /* produce a scroll of mail */
341 struct obj *obj;
342 struct monst *md;
343 extern char plname[];
344 extern struct obj *mksobj();
345 extern struct monst *makemon();
346 extern struct permonst pm_mail_daemon;
347
348 obj = mksobj(SCR_MAIL);
349 if(md = makemon(&pm_mail_daemon, u.ux, u.uy)) /* always succeeds */
350 mdrush(md,0);
351
352 pline("\"Hello, %s! I have some mail for you.\"", plname);
353 if(md) {
354 if(dist(md->mx,md->my) > 2)
355 pline("\"Catch!\"");
356 more();
357
358 /* let him disappear again */
359 mdrush(md,1);
360 mondead(md);
361 }
362
363 obj = addinv(obj);
364 (void) identify(obj); /* set known and do prinv() */
365 }
366
367 /* make md run through the cave */
368 static void
mdrush(struct monst * md,boolean away)369 mdrush(struct monst *md, boolean away)
370 {
371 int uroom = inroom(u.ux, u.uy);
372 if(uroom >= 0) {
373 int tmp = rooms[uroom].fdoor;
374 int cnt = rooms[uroom].doorct;
375 int fx = u.ux, fy = u.uy;
376 while(cnt--) {
377 if(dist(fx,fy) < dist(doors[tmp].x, doors[tmp].y)){
378 fx = doors[tmp].x;
379 fy = doors[tmp].y;
380 }
381 tmp++;
382 }
383 tmp_at(-1, md->data->mlet); /* open call */
384 if(away) { /* interchange origin and destination */
385 unpmon(md);
386 tmp = fx; fx = md->mx; md->mx = tmp;
387 tmp = fy; fy = md->my; md->my = tmp;
388 }
389 while(fx != md->mx || fy != md->my) {
390 int dx,dy,nfx = fx,nfy = fy,d1,d2;
391
392 tmp_at(fx,fy);
393 d1 = DIST(fx,fy,md->mx,md->my);
394 for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
395 if(dx || dy) {
396 d2 = DIST(fx+dx,fy+dy,md->mx,md->my);
397 if(d2 < d1) {
398 d1 = d2;
399 nfx = fx+dx;
400 nfy = fy+dy;
401 }
402 }
403 if(nfx != fx || nfy != fy) {
404 fx = nfx;
405 fy = nfy;
406 } else {
407 if(!away) {
408 md->mx = fx;
409 md->my = fy;
410 }
411 break;
412 }
413 }
414 tmp_at(-1,-1); /* close call */
415 }
416 if(!away)
417 pmon(md);
418 }
419
420 void
readmail(void)421 readmail(void)
422 {
423 #ifdef DEF_MAILREADER /* This implies that UNIX is defined */
424 char *mr = 0;
425 more();
426 if(!(mr = getenv("MAILREADER")))
427 mr = DEF_MAILREADER;
428 if(child(1)){
429 execl(mr, mr, (char *)NULL);
430 exit(1);
431 }
432 #else /* DEF_MAILREADER */
433 (void) page_file(mailbox, FALSE);
434 #endif /* DEF_MAILREADER */
435 /* get new stat; not entirely correct: there is a small time
436 window where we do not see new mail */
437 getmailstatus();
438 }
439 #endif /* MAIL */
440
441 /* normalize file name - we don't like ..'s or /'s */
442 void
regularize(char * s)443 regularize(char *s)
444 {
445 char *lp;
446
447 while((lp = strchr(s, '.')) || (lp = strchr(s, '/')))
448 *lp = '_';
449 }
450