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