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