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