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