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