1 /* $NetBSD: hack.main.c,v 1.15 2010/02/03 15:34:38 roy 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.15 2010/02/03 15:34:38 roy 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 94 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ 95 96 int main(int, char *[]); 97 static void chdirx(const char *, boolean); 98 99 int 100 main(int argc, char *argv[]) 101 { 102 int fd; 103 #ifdef CHDIR 104 char *dir; 105 #endif 106 107 /* Check for dirty tricks with closed fds 0, 1, 2 */ 108 fd = open("/dev/null", O_RDONLY); 109 if (fd < 3) 110 exit(1); 111 close(fd); 112 113 hname = argv[0]; 114 hackpid = getpid(); 115 116 #ifdef CHDIR /* otherwise no chdir() */ 117 /* 118 * See if we must change directory to the playground. 119 * (Perhaps hack runs suid and playground is inaccessible 120 * for the player.) 121 * The environment variable HACKDIR is overridden by a 122 * -d command line option (must be the first option given) 123 */ 124 125 dir = getenv("HACKDIR"); 126 if (argc > 1 && !strncmp(argv[1], "-d", 2)) { 127 argc--; 128 argv++; 129 dir = argv[0] + 2; 130 if (*dir == '=' || *dir == ':') 131 dir++; 132 if (!*dir && argc > 1) { 133 argc--; 134 argv++; 135 dir = argv[0]; 136 } 137 if (!*dir) 138 error("Flag -d must be followed by a directory name."); 139 } 140 #endif 141 142 /* 143 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS 144 * 2. Use $USER or $LOGNAME (if 1. fails) 145 * 3. Use getlogin() (if 2. fails) 146 * The resulting name is overridden by command line options. 147 * If everything fails, or if the resulting name is some generic 148 * account like "games", "play", "player", "hack" then eventually 149 * we'll ask him. 150 * Note that we trust him here; it is possible to play under 151 * somebody else's name. 152 */ 153 { 154 char *s; 155 156 initoptions(); 157 if (!*plname && (s = getenv("USER"))) 158 (void) strncpy(plname, s, sizeof(plname) - 1); 159 if (!*plname && (s = getenv("LOGNAME"))) 160 (void) strncpy(plname, s, sizeof(plname) - 1); 161 if (!*plname && (s = getlogin())) 162 (void) strncpy(plname, s, sizeof(plname) - 1); 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 * It seems he really wants to play. 178 * Remember tty modes, to be restored on exit. 179 */ 180 gettty(); 181 setbuf(stdout, obuf); 182 setrandom(); 183 startup(); 184 cls(); 185 u.uhp = 1; /* prevent RIP on early quits */ 186 u.ux = FAR; /* prevent nscr() */ 187 (void) signal(SIGHUP, hang_up); 188 189 /* 190 * Find the creation date of this game, 191 * so as to avoid restoring outdated savefiles. 192 */ 193 gethdate(hname); 194 195 /* 196 * We cannot do chdir earlier, otherwise gethdate will fail. 197 */ 198 #ifdef CHDIR 199 chdirx(dir, 1); 200 #endif 201 202 /* 203 * Process options. 204 */ 205 while (argc > 1 && argv[1][0] == '-') { 206 argv++; 207 argc--; 208 switch (argv[0][1]) { 209 #ifdef WIZARD 210 case 'D': 211 /* if(!strcmp(getlogin(), WIZARD)) */ 212 wizard = TRUE; 213 /* 214 * else printf("Sorry.\n"); 215 */ 216 break; 217 #endif 218 #ifdef NEWS 219 case 'n': 220 flags.nonews = TRUE; 221 break; 222 #endif 223 case 'u': 224 if (argv[0][2]) 225 (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1); 226 else if (argc > 1) { 227 argc--; 228 argv++; 229 (void) strncpy(plname, argv[0], sizeof(plname) - 1); 230 } else 231 printf("Player name expected after -u\n"); 232 break; 233 default: 234 /* allow -T for Tourist, etc. */ 235 (void) strncpy(pl_character, argv[0] + 1, 236 sizeof(pl_character) - 1); 237 238 /* printf("Unknown option: %s\n", *argv); */ 239 } 240 } 241 242 if (argc > 1) 243 locknum = atoi(argv[1]); 244 #ifdef MAX_NR_OF_PLAYERS 245 if (!locknum || locknum > MAX_NR_OF_PLAYERS) 246 locknum = MAX_NR_OF_PLAYERS; 247 #endif 248 #ifdef DEF_PAGER 249 if (((catmore = getenv("HACKPAGER")) == NULL && 250 (catmore = getenv("PAGER")) == NULL) || 251 catmore[0] == '\0') 252 catmore = DEF_PAGER; 253 #endif 254 #ifdef MAIL 255 getmailstatus(); 256 #endif 257 #ifdef WIZARD 258 if (wizard) 259 (void) strcpy(plname, "wizard"); 260 else 261 #endif 262 if (!*plname || !strncmp(plname, "player", 4) 263 || !strncmp(plname, "games", 4)) 264 askname(); 265 plnamesuffix(); /* strip suffix from name; calls askname() */ 266 /* again if suffix was whole name */ 267 /* accepts any suffix */ 268 #ifdef WIZARD 269 if (!wizard) { 270 #endif 271 /* 272 * check for multiple games under the same name 273 * (if !locknum) or check max nr of players (otherwise) 274 */ 275 (void) signal(SIGQUIT, SIG_IGN); 276 (void) signal(SIGINT, SIG_IGN); 277 if (!locknum) 278 (void) strcpy(lock, plname); 279 getlock(); /* sets lock if locknum != 0 */ 280 #ifdef WIZARD 281 } else { 282 char *sfoo; 283 (void) strcpy(lock, plname); 284 if ((sfoo = getenv("MAGIC")) != NULL) 285 while (*sfoo) { 286 switch (*sfoo++) { 287 case 'n': 288 (void) srandom(*sfoo++); 289 break; 290 } 291 } 292 if ((sfoo = getenv("GENOCIDED")) != NULL) { 293 if (*sfoo == '!') { 294 const struct permonst *pm = mons; 295 char *gp = genocided; 296 297 while (pm < mons + CMNUM + 2) { 298 if (!strchr(sfoo, pm->mlet)) 299 *gp++ = pm->mlet; 300 pm++; 301 } 302 *gp = 0; 303 } else 304 (void) strlcpy(genocided, sfoo, 305 sizeof(genocided)); 306 (void) strcpy(fut_geno, genocided); 307 } 308 } 309 #endif 310 setftty(); 311 (void) snprintf(SAVEF, sizeof(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(int foo) 481 { 482 /* construct the string xlock.n */ 483 size_t pos; 484 485 pos = 0; 486 while (lock[pos] && lock[pos] != '.') 487 pos++; 488 (void) snprintf(lock + pos, sizeof(lock) - pos, ".%d", foo); 489 } 490 491 /* 492 * plname is filled either by an option (-u Player or -uPlayer) or 493 * explicitly (-w implies wizard) or by askname. 494 * It may still contain a suffix denoting pl_character. 495 */ 496 void 497 askname(void) 498 { 499 int c, ct; 500 printf("\nWho are you? "); 501 (void) fflush(stdout); 502 ct = 0; 503 while ((c = getchar()) != '\n') { 504 if (c == EOF) 505 error("End of input\n"); 506 /* some people get confused when their erase char is not ^H */ 507 if (c == '\010') { 508 if (ct) 509 ct--; 510 continue; 511 } 512 if (c != '-') 513 if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z') 514 c = '_'; 515 if (ct < (int)sizeof(plname) - 1) 516 plname[ct++] = c; 517 } 518 plname[ct] = 0; 519 if (ct == 0) 520 askname(); 521 } 522 523 /* VARARGS1 */ 524 void 525 impossible(const char *s, ...) 526 { 527 va_list ap; 528 529 va_start(ap, s); 530 vpline(s, ap); 531 va_end(ap); 532 pline("Program in disorder - perhaps you'd better Quit."); 533 } 534 535 #ifdef CHDIR 536 static void 537 chdirx(const char *dir, boolean wr) 538 { 539 540 #ifdef SECURE 541 if (dir /* User specified directory? */ 542 #ifdef HACKDIR 543 && strcmp(dir, HACKDIR) /* and not the default? */ 544 #endif 545 ) { 546 (void) setuid(getuid()); /* Ron Wessels */ 547 (void) setgid(getgid()); 548 } 549 #endif 550 551 #ifdef HACKDIR 552 if (dir == NULL) 553 dir = HACKDIR; 554 #endif 555 556 if (dir && chdir(dir) < 0) { 557 perror(dir); 558 error("Cannot chdir to %s.", dir); 559 } 560 /* warn the player if he cannot write the record file */ 561 /* perhaps we should also test whether . is writable */ 562 /* unfortunately the access systemcall is worthless */ 563 if (wr) { 564 int fd; 565 566 if (dir == NULL) 567 dir = "."; 568 if ((fd = open(RECORD, O_RDWR)) < 0) { 569 printf("Warning: cannot write %s/%s", dir, RECORD); 570 getret(); 571 } else 572 (void) close(fd); 573 } 574 } 575 #endif 576 577 void 578 stop_occupation(void) 579 { 580 if (occupation) { 581 pline("You stop %s.", occtxt); 582 occupation = 0; 583 } 584 } 585