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