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