1 /* $OpenBSD: hack.main.c,v 1.15 2009/10/27 23:59:25 deraadt 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 #include <sys/types.h> 65 #include <sys/stat.h> 66 #include <stdlib.h> 67 #include <stdio.h> 68 #include <stdarg.h> 69 #include <signal.h> 70 #include <unistd.h> 71 #include "hack.h" 72 73 #ifdef QUEST 74 #define gamename "quest" 75 #else 76 #define gamename "hack" 77 #endif 78 79 extern char plname[PL_NSIZ], pl_character[PL_CSIZ]; 80 extern struct permonst mons[CMNUM+2]; 81 extern char genocided[60], fut_geno[60]; 82 83 void (*afternmv)(void); 84 int (*occupation)(void); 85 char *occtxt; /* defined when occupation != NULL */ 86 87 int hackpid; /* current pid */ 88 int locknum; /* max num of players */ 89 #ifdef DEF_PAGER 90 char *catmore; /* default pager */ 91 #endif 92 char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */ 93 char *hname; /* name of the game (argv[0] of call) */ 94 char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ 95 96 extern char *nomovemsg; 97 extern long wailmsg; 98 99 #ifdef CHDIR 100 static void chdirx(char *, boolean); 101 #endif 102 103 int 104 main(int argc, char **argv) 105 { 106 int fd; 107 #ifdef CHDIR 108 char *dir; 109 #endif 110 111 hname = argv[0]; 112 hackpid = getpid(); 113 114 #ifdef CHDIR /* otherwise no chdir() */ 115 /* 116 * See if we must change directory to the playground. 117 * (Perhaps hack runs suid and playground is inaccessible 118 * for the player.) 119 * The environment variable HACKDIR is overridden by a 120 * -d command line option (must be the first option given) 121 */ 122 123 dir = getenv("HACKDIR"); 124 if(argc > 1 && !strncmp(argv[1], "-d", 2)) { 125 argc--; 126 argv++; 127 dir = argv[0]+2; 128 if(*dir == '=' || *dir == ':') dir++; 129 if(!*dir && argc > 1) { 130 argc--; 131 argv++; 132 dir = argv[0]; 133 } 134 if(!*dir) 135 error("Flag -d must be followed by a directory name."); 136 } 137 #endif 138 139 /* 140 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS 141 * 2. Use $LOGNAME or $USER (if 1. fails) 142 * 3. Use getlogin() (if 2. fails) 143 * The resulting name is overridden by command line options. 144 * If everything fails, or if the resulting name is some generic 145 * account like "games", "play", "player", "hack" then eventually 146 * we'll ask him. 147 * Note that we trust him here; it is possible to play under 148 * somebody else's name. 149 */ 150 { char *s; 151 152 initoptions(); 153 if(!*plname && (s = getenv("LOGNAME"))) 154 (void) strlcpy(plname, s, sizeof(plname)); 155 if(!*plname && (s = getenv("USER"))) 156 (void) strlcpy(plname, s, sizeof(plname)); 157 if(!*plname && (s = getlogin())) 158 (void) strlcpy(plname, s, sizeof(plname)); 159 } 160 161 /* 162 * Now we know the directory containing 'record' and 163 * may do a prscore(). 164 */ 165 if(argc > 1 && !strncmp(argv[1], "-s", 2)) { 166 #ifdef CHDIR 167 chdirx(dir,0); 168 #endif 169 prscore(argc, argv); 170 exit(0); 171 } 172 173 /* 174 * It seems he really wants to play. 175 * Remember tty modes, to be restored on exit. 176 */ 177 gettty(); 178 setbuf(stdout,obuf); 179 umask(007); 180 srandomdev(); 181 startup(); 182 cls(); 183 u.uhp = 1; /* prevent RIP on early quits */ 184 u.ux = FAR; /* prevent nscr() */ 185 (void) signal(SIGHUP, hackhangup); 186 187 /* 188 * Find the creation date of this game, 189 * so as to avoid restoring outdated savefiles. 190 */ 191 gethdate(hname); 192 193 /* 194 * We cannot do chdir earlier, otherwise gethdate will fail. 195 */ 196 #ifdef CHDIR 197 chdirx(dir,1); 198 #endif 199 200 /* 201 * Process options. 202 */ 203 while(argc > 1 && argv[1][0] == '-'){ 204 argv++; 205 argc--; 206 switch(argv[0][1]){ 207 #ifdef WIZARD 208 case 'D': 209 /* if(!strcmp(getlogin(), WIZARD)) */ 210 wizard = TRUE; 211 /* else 212 printf("Sorry.\n"); */ 213 break; 214 #endif 215 #ifdef NEWS 216 case 'n': 217 flags.nonews = TRUE; 218 break; 219 #endif 220 case 'u': 221 if(argv[0][2]) { 222 (void) strlcpy(plname, argv[0]+2, sizeof(plname)); 223 } else if(argc > 1) { 224 argc--; 225 argv++; 226 (void) strlcpy(plname, argv[0], sizeof(plname)); 227 } else 228 printf("Player name expected after -u\n"); 229 break; 230 default: 231 /* allow -T for Tourist, etc. */ 232 (void) strlcpy(pl_character, argv[0]+1, sizeof(pl_character)); 233 /* printf("Unknown option: %s\n", *argv); */ 234 } 235 } 236 237 if(argc > 1) 238 locknum = atoi(argv[1]); 239 #ifdef MAX_NR_OF_PLAYERS 240 if(!locknum || locknum > MAX_NR_OF_PLAYERS) 241 locknum = MAX_NR_OF_PLAYERS; 242 #endif 243 #ifdef DEF_PAGER 244 if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER"))) 245 catmore = DEF_PAGER; 246 #endif 247 #ifdef MAIL 248 getmailstatus(); 249 #endif 250 #ifdef WIZARD 251 if(wizard) (void) strlcpy(plname, "wizard", sizeof plname); else 252 #endif 253 if(!*plname || !strncmp(plname, "player", 4) 254 || !strncmp(plname, "games", 4)) 255 askname(); 256 plnamesuffix(); /* strip suffix from name; calls askname() */ 257 /* again if suffix was whole name */ 258 /* accepts any suffix */ 259 #ifdef WIZARD 260 if(!wizard) { 261 #endif 262 /* 263 * check for multiple games under the same name 264 * (if !locknum) or check max nr of players (otherwise) 265 */ 266 (void) signal(SIGQUIT,SIG_IGN); 267 (void) signal(SIGINT,SIG_IGN); 268 if(!locknum) 269 (void) strlcpy(lock,plname,sizeof lock); 270 getlock(); /* sets lock if locknum != 0 */ 271 #ifdef WIZARD 272 } else { 273 char *sfoo; 274 (void) strlcpy(lock,plname,sizeof lock); 275 if ((sfoo = getenv("MAGIC"))) 276 while(*sfoo) { 277 switch(*sfoo++) { 278 case 'n': (void) srandom(*sfoo++); 279 break; 280 } 281 } 282 if ((sfoo = getenv("GENOCIDED"))) { 283 if(*sfoo == '!'){ 284 struct permonst *pm = mons; 285 char *gp = genocided; 286 287 while(pm < mons+CMNUM+2){ 288 if(!strchr(sfoo, pm->mlet)) 289 *gp++ = pm->mlet; 290 pm++; 291 } 292 *gp = 0; 293 } else 294 strlcpy(genocided, sfoo, sizeof genocided); 295 strlcpy(fut_geno, genocided, sizeof fut_geno); 296 } 297 } 298 #endif 299 setftty(); 300 (void) snprintf(SAVEF, sizeof SAVEF, "save/%u%s", getuid(), plname); 301 regularize(SAVEF+5); /* avoid . or / in name */ 302 if((fd = open(SAVEF, O_RDONLY)) >= 0 && 303 (uptodate(fd) || unlink(SAVEF) == 666)) { 304 (void) signal(SIGINT,done1); 305 pline("Restoring old save file..."); 306 (void) fflush(stdout); 307 if(!dorecover(fd)) 308 goto not_recovered; 309 pline("Hello %s, welcome to %s!", plname, gamename); 310 flags.move = 0; 311 } else { 312 not_recovered: 313 fobj = fcobj = invent = 0; 314 fmon = fallen_down = 0; 315 ftrap = 0; 316 fgold = 0; 317 flags.ident = 1; 318 init_objects(); 319 u_init(); 320 321 (void) signal(SIGINT,done1); 322 mklev(); 323 u.ux = xupstair; 324 u.uy = yupstair; 325 (void) inshop(); 326 setsee(); 327 flags.botlx = 1; 328 makedog(); 329 { struct monst *mtmp; 330 if ((mtmp = m_at(u.ux, u.uy))) 331 mnexto(mtmp); /* riv05!a3 */ 332 } 333 seemons(); 334 #ifdef NEWS 335 if(flags.nonews || !readnews()) 336 /* after reading news we did docrt() already */ 337 #endif 338 docrt(); 339 340 /* give welcome message before pickup messages */ 341 pline("Hello %s, welcome to %s!", plname, gamename); 342 343 pickup(1); 344 read_engr_at(u.ux,u.uy); 345 flags.move = 1; 346 } 347 348 flags.moonphase = phase_of_the_moon(); 349 if(flags.moonphase == FULL_MOON) { 350 pline("You are lucky! Full moon tonight."); 351 u.uluck++; 352 } else if(flags.moonphase == NEW_MOON) { 353 pline("Be careful! New moon tonight."); 354 } 355 356 initrack(); 357 358 for(;;) { 359 if(flags.move) { /* actual time passed */ 360 361 settrack(); 362 363 if(moves%2 == 0 || 364 (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) { 365 extern struct monst *makemon(); 366 movemon(); 367 if(!rn2(70)) 368 (void) makemon((struct permonst *)0, 0, 0); 369 } 370 if(Glib) glibr(); 371 hacktimeout(); 372 ++moves; 373 if(flags.time) flags.botl = 1; 374 if(u.uhp < 1) { 375 pline("You die..."); 376 done("died"); 377 } 378 if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){ 379 wailmsg = moves; 380 if(u.uhp == 1) 381 pline("You hear the wailing of the Banshee..."); 382 else 383 pline("You hear the howling of the CwnAnnwn..."); 384 } 385 if(u.uhp < u.uhpmax) { 386 if(u.ulevel > 9) { 387 if(Regeneration || !(moves%3)) { 388 flags.botl = 1; 389 u.uhp += rnd((int) u.ulevel-9); 390 if(u.uhp > u.uhpmax) 391 u.uhp = u.uhpmax; 392 } 393 } else if(Regeneration || 394 (!(moves%(22-u.ulevel*2)))) { 395 flags.botl = 1; 396 u.uhp++; 397 } 398 } 399 if(Teleportation && !rn2(85)) tele(); 400 if(Searching && multi >= 0) (void) dosearch(); 401 gethungry(); 402 invault(); 403 amulet(); 404 } 405 if(multi < 0) { 406 if(!++multi){ 407 pline(nomovemsg ? nomovemsg : 408 "You can move again."); 409 nomovemsg = 0; 410 if(afternmv) (*afternmv)(); 411 afternmv = 0; 412 } 413 } 414 415 find_ac(); 416 #ifndef QUEST 417 if(!flags.mv || Blind) 418 #endif 419 { 420 seeobjs(); 421 seemons(); 422 nscr(); 423 } 424 if(flags.botl || flags.botlx) bot(); 425 426 flags.move = 1; 427 428 if(multi >= 0 && occupation) { 429 if(monster_nearby()) 430 stop_occupation(); 431 else if ((*occupation)() == 0) 432 occupation = 0; 433 continue; 434 } 435 436 if(multi > 0) { 437 #ifdef QUEST 438 if(flags.run >= 4) finddir(); 439 #endif 440 lookaround(); 441 if(!multi) { /* lookaround may clear multi */ 442 flags.move = 0; 443 continue; 444 } 445 if(flags.mv) { 446 if(multi < COLNO && !--multi) 447 flags.mv = flags.run = 0; 448 domove(); 449 } else { 450 --multi; 451 rhack(save_cm); 452 } 453 } else if(multi == 0) { 454 #ifdef MAIL 455 ckmailstatus(); 456 #endif 457 rhack((char *) 0); 458 } 459 if(multi && multi%7 == 0) 460 (void) fflush(stdout); 461 } 462 } 463 464 void 465 glo(int foo) 466 { 467 /* construct the string xlock.n */ 468 char *tf; 469 470 tf = lock; 471 while(*tf && *tf != '.') tf++; 472 (void) snprintf(tf, lock + sizeof lock - tf, ".%d", foo); 473 } 474 475 /* 476 * plname is filled either by an option (-u Player or -uPlayer) or 477 * explicitly (-w implies wizard) or by askname. 478 * It may still contain a suffix denoting pl_character. 479 */ 480 void 481 askname() 482 { 483 int c,ct; 484 485 printf("\nWho are you? "); 486 (void) fflush(stdout); 487 ct = 0; 488 while((c = getchar()) != '\n'){ 489 if(c == EOF) error("End of input\n"); 490 /* some people get confused when their erase char is not ^H */ 491 if(c == '\010') { 492 if(ct) ct--; 493 continue; 494 } 495 if(c != '-') 496 if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_'; 497 if(ct < sizeof(plname)-1) plname[ct++] = c; 498 } 499 plname[ct] = 0; 500 if(ct == 0) askname(); 501 } 502 503 void 504 impossible(char *s, ...) 505 { 506 va_list ap; 507 508 va_start(ap, s); 509 pline(s, ap); 510 va_end(ap); 511 pline("Program in disorder - perhaps you'd better Quit."); 512 } 513 514 #ifdef CHDIR 515 static void 516 chdirx(char *dir, boolean wr) 517 { 518 gid_t gid; 519 520 #ifdef SECURE 521 if(dir /* User specified directory? */ 522 #ifdef HACKDIR 523 && strcmp(dir, HACKDIR) /* and not the default? */ 524 #endif 525 ) { 526 /* revoke privs */ 527 gid = getgid(); 528 setresgid(gid, gid, gid); 529 } 530 #endif 531 532 #ifdef HACKDIR 533 if(dir == NULL) 534 dir = HACKDIR; 535 #endif 536 537 if(dir && chdir(dir) < 0) { 538 perror(dir); 539 error("Cannot chdir to %s.", dir); 540 } 541 542 /* warn the player if he cannot write the record file */ 543 /* perhaps we should also test whether . is writable */ 544 /* unfortunately the access systemcall is worthless */ 545 if(wr) { 546 int fd; 547 548 if(dir == NULL) 549 dir = "."; 550 if((fd = open(RECORD, O_RDWR)) < 0) { 551 printf("Warning: cannot write %s/%s", dir, RECORD); 552 getret(); 553 } else 554 (void) close(fd); 555 } 556 } 557 #endif 558 559 void 560 stop_occupation() 561 { 562 if(occupation) { 563 pline("You stop %s.", occtxt); 564 occupation = 0; 565 } 566 } 567