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