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