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