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