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