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