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