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