1/* SCCS Id: @(#)unixmain.c 3.4 1997/01/22 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5/* main.c - Unix NetHack */ 6 7#include "hack.h" 8#include "dlb.h" 9#include <assert.h> 10 11#include <sys/stat.h> 12#include <signal.h> 13#include <pwd.h> 14#ifndef O_RDONLY 15#include <fcntl.h> 16#endif 17 18#if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) 19# if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) 20# if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) 21extern struct passwd *FDECL(getpwuid,(uid_t)); 22# else 23extern struct passwd *FDECL(getpwuid,(int)); 24# endif 25# endif 26#endif 27extern struct passwd *FDECL(getpwnam,(const char *)); 28#ifdef CHDIR 29static void FDECL(chdirx, (const char *,BOOLEAN_P)); 30#endif /* CHDIR */ 31static boolean NDECL(whoami); 32static void FDECL(process_options, (int, char **)); 33 34#ifdef _M_UNIX 35extern void NDECL(check_sco_console); 36extern void NDECL(init_sco_cons); 37#endif 38#if defined(__linux__) && REFOS_LINUX /* Don't define linux in RefOS by default */ 39extern void NDECL(check_linux_console); 40extern void NDECL(init_linux_cons); 41#endif 42 43static void NDECL(wd_message); 44#ifdef WIZARD 45static boolean wiz_error_flag = FALSE; 46#endif 47 48int 49nethack_main(argc,argv) 50int argc; 51char *argv[]; 52{ 53 register int fd; 54#ifdef CHDIR 55 register char *dir; 56#endif 57 boolean exact_username; 58 59#if defined(__APPLE__) 60 /* special hack to change working directory to a resource fork when 61 running from finder --sam */ 62#define MAC_PATH_VALUE ".app/Contents/MacOS/" 63 char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; 64 int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len=0; 65 getcwd(mac_cwd, 1024); 66 if(mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { 67 if((mac_exe = strrchr(mac_exe, '/'))) 68 mac_exe++; 69 else 70 mac_exe = argv[0]; 71 mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); 72 if(mac_tmp_len <= arg0_len) { 73 mac_tmp = malloc(mac_tmp_len + 1); 74 sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); 75 if(!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { 76 mac_lhs_len = (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; 77 if(mac_lhs_len > mac_tmp_len - 1) 78 mac_tmp = realloc(mac_tmp, mac_lhs_len); 79 strncpy(mac_tmp, argv[0], mac_lhs_len); 80 mac_tmp[mac_lhs_len] = '\0'; 81 chdir(mac_tmp); 82 } 83 free(mac_tmp); 84 } 85 } 86#endif 87 88 hname = argv[0]; 89 hackpid = getpid(); 90 (void) umask(0777 & ~FCMASK); 91 92 choose_windows(DEFAULT_WINDOW_SYS); 93 94#ifdef CHDIR /* otherwise no chdir() */ 95 /* 96 * See if we must change directory to the playground. 97 * (Perhaps hack runs suid and playground is inaccessible 98 * for the player.) 99 * The environment variable HACKDIR is overridden by a 100 * -d command line option (must be the first option given) 101 */ 102 dir = nh_getenv("NETHACKDIR"); 103 if (!dir) dir = nh_getenv("HACKDIR"); 104#endif 105 if(argc > 1) { 106#ifdef CHDIR 107 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { 108 /* avoid matching "-dec" for DECgraphics; since the man page 109 * says -d directory, hope nobody's using -desomething_else 110 */ 111 argc--; 112 argv++; 113 dir = argv[0]+2; 114 if(*dir == '=' || *dir == ':') dir++; 115 if(!*dir && argc > 1) { 116 argc--; 117 argv++; 118 dir = argv[0]; 119 } 120 if(!*dir) 121 error("Flag -d must be followed by a directory name."); 122 } 123 if (argc > 1) 124#endif /* CHDIR */ 125 126 /* 127 * Now we know the directory containing 'record' and 128 * may do a prscore(). Exclude `-style' - it's a Qt option. 129 */ 130 if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) { 131#ifdef CHDIR 132 chdirx(dir,0); 133#endif 134 prscore(argc, argv); 135 exit(EXIT_SUCCESS); 136 } 137 } 138 139 /* 140 * Change directories before we initialize the window system so 141 * we can find the tile file. 142 */ 143#ifdef CHDIR 144 chdirx(dir,1); 145#endif 146 147#ifdef _M_UNIX 148 check_sco_console(); 149#endif 150#if defined(__linux__) && REFOS_LINUX /* Don't define linux in RefOS by default */ 151 check_linux_console(); 152#endif 153 initoptions(); 154 init_nhwindows(&argc,argv); 155 exact_username = whoami(); 156#ifdef _M_UNIX 157 init_sco_cons(); 158#endif 159#if defined(__linux__) && REFOS_LINUX /* Don't define linux in RefOS by default */ 160 init_linux_cons(); 161#endif 162 163 /* 164 * It seems you really want to play. 165 */ 166 u.uhp = 1; /* prevent RIP on early quits */ 167 (void) signal(SIGHUP, (SIG_RET_TYPE) hangup); 168#ifdef SIGXCPU 169 (void) signal(SIGXCPU, (SIG_RET_TYPE) hangup); 170#endif 171 172 process_options(argc, argv); /* command line options */ 173 174#ifdef DEF_PAGER 175 if(!(catmore = nh_getenv("HACKPAGER")) && !(catmore = nh_getenv("PAGER"))) 176 catmore = DEF_PAGER; 177#endif 178#ifdef MAIL 179 getmailstatus(); 180#endif 181#ifdef WIZARD 182 if (wizard) 183 Strcpy(plname, "wizard"); 184 else 185#endif 186 if(!*plname || !strncmp(plname, "player", 4) 187 || !strncmp(plname, "games", 4)) { 188 askname(); 189 } else if (exact_username) { 190 /* guard against user names with hyphens in them */ 191 int len = strlen(plname); 192 /* append the current role, if any, so that last dash is ours */ 193 if (++len < sizeof plname) 194 (void)strncat(strcat(plname, "-"), 195 pl_character, sizeof plname - len - 1); 196 } 197 198 plnamesuffix(); /* strip suffix from name; calls askname() */ 199 /* again if suffix was whole name */ 200 /* accepts any suffix */ 201 202#ifdef WIZARD 203 if(!wizard) { 204#endif 205 /* 206 * check for multiple games under the same name 207 * (if !locknum) or check max nr of players (otherwise) 208 */ 209 (void) signal(SIGQUIT,SIG_IGN); 210 (void) signal(SIGINT,SIG_IGN); 211 if(!locknum) 212 Sprintf(lock, "%d%s", (int)getuid(), plname); 213 getlock(); 214#ifdef WIZARD 215 } else { 216 Sprintf(lock, "%d%s", (int)getuid(), plname); 217 getlock(); 218 219 } 220#endif /* WIZARD */ 221 222 dlb_init(); /* must be before newgame() */ 223 224 /* 225 * Initialization of the boundaries of the mazes 226 * Both boundaries have to be even. 227 */ 228 x_maze_max = COLNO-1; 229 if (x_maze_max % 2) 230 x_maze_max--; 231 y_maze_max = ROWNO-1; 232 if (y_maze_max % 2) 233 y_maze_max--; 234 235 /* 236 * Initialize the vision system. This must be before mklev() on a 237 * new game or before a level restore on a saved game. 238 */ 239 vision_init(); 240 241 display_gamewindows(); 242 243 if ((fd = restore_saved_game()) >= 0) { 244#ifdef WIZARD 245 /* Since wizard is actually flags.debug, restoring might 246 * overwrite it. 247 */ 248 boolean remember_wiz_mode = wizard; 249#endif 250 const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1); 251 252 (void) chmod(fq_save,0); /* disallow parallel restores */ 253 (void) signal(SIGINT, (SIG_RET_TYPE) done1); 254#ifdef NEWS 255 if(iflags.news) { 256 display_file(NEWS, FALSE); 257 iflags.news = FALSE; /* in case dorecover() fails */ 258 } 259#endif 260 pline("Restoring save file..."); 261 mark_synch(); /* flush output */ 262 if(!dorecover(fd)) 263 goto not_recovered; 264#ifdef WIZARD 265 if(!wizard && remember_wiz_mode) wizard = TRUE; 266#endif 267 check_special_room(FALSE); 268 wd_message(); 269 270 if (discover || wizard) { 271 if(yn("Do you want to keep the save file?") == 'n') 272 (void) delete_savefile(); 273 else { 274 (void) chmod(fq_save,FCMASK); /* back to readable */ 275 compress(fq_save); 276 } 277 } 278 flags.move = 0; 279 } else { 280not_recovered: 281 player_selection(); 282 newgame(); 283 wd_message(); 284 285 flags.move = 0; 286 set_wear(); 287 (void) pickup(1); 288 } 289 290 moveloop(); 291 exit(EXIT_SUCCESS); 292 /*NOTREACHED*/ 293 return(0); 294} 295 296static void 297process_options(argc, argv) 298int argc; 299char *argv[]; 300{ 301 int i; 302 303 304 /* 305 * Process options. 306 */ 307 while(argc > 1 && argv[1][0] == '-'){ 308 argv++; 309 argc--; 310 switch(argv[0][1]){ 311 case 'D': 312#ifdef WIZARD 313 { 314#if 0 315 char *user; 316 int uid; 317 struct passwd *pw = (struct passwd *)0; 318 319 uid = getuid(); 320 user = getlogin(); 321 if (user) { 322 pw = getpwnam(user); 323 if (pw && (pw->pw_uid != uid)) pw = 0; 324 } 325 if (pw == 0) { 326 user = nh_getenv("USER"); 327 if (user) { 328 pw = getpwnam(user); 329 if (pw && (pw->pw_uid != uid)) pw = 0; 330 } 331 if (pw == 0) { 332 pw = getpwuid(uid); 333 } 334 } 335 if (pw && !strcmp(pw->pw_name,WIZARD)) { 336 wizard = TRUE; 337 break; 338 } 339#endif 340 // RefOS hack: we don't really support a passwd structure, so simple enable 341 // wizard mode when asked to do so. 342 wizard = TRUE; 343 } 344 /* otherwise fall thru to discover */ 345 wiz_error_flag = TRUE; 346#endif 347 case 'X': 348 discover = TRUE; 349 break; 350#ifdef NEWS 351 case 'n': 352 iflags.news = FALSE; 353 break; 354#endif 355 case 'u': 356 if(argv[0][2]) 357 (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); 358 else if(argc > 1) { 359 argc--; 360 argv++; 361 (void) strncpy(plname, argv[0], sizeof(plname)-1); 362 } else 363 raw_print("Player name expected after -u"); 364 break; 365 case 'I': 366 case 'i': 367 if (!strncmpi(argv[0]+1, "IBM", 3)) 368 switch_graphics(IBM_GRAPHICS); 369 break; 370 /* case 'D': */ 371 case 'd': 372 if (!strncmpi(argv[0]+1, "DEC", 3)) 373 switch_graphics(DEC_GRAPHICS); 374 break; 375 case 'p': /* profession (role) */ 376 if (argv[0][2]) { 377 if ((i = str2role(&argv[0][2])) >= 0) 378 flags.initrole = i; 379 } else if (argc > 1) { 380 argc--; 381 argv++; 382 if ((i = str2role(argv[0])) >= 0) 383 flags.initrole = i; 384 } 385 break; 386 case 'r': /* race */ 387 if (argv[0][2]) { 388 if ((i = str2race(&argv[0][2])) >= 0) 389 flags.initrace = i; 390 } else if (argc > 1) { 391 argc--; 392 argv++; 393 if ((i = str2race(argv[0])) >= 0) 394 flags.initrace = i; 395 } 396 break; 397 case '@': 398 flags.randomall = 1; 399 break; 400 default: 401 if ((i = str2role(&argv[0][1])) >= 0) { 402 flags.initrole = i; 403 break; 404 } 405 /* else raw_printf("Unknown option: %s", *argv); */ 406 } 407 } 408 409 if(argc > 1) 410 locknum = atoi(argv[1]); 411#ifdef MAX_NR_OF_PLAYERS 412 if(!locknum || locknum > MAX_NR_OF_PLAYERS) 413 locknum = MAX_NR_OF_PLAYERS; 414#endif 415} 416 417#ifdef CHDIR 418static void 419chdirx(dir, wr) 420const char *dir; 421boolean wr; 422{ 423 if (dir /* User specified directory? */ 424# ifdef HACKDIR 425 && strcmp(dir, HACKDIR) /* and not the default? */ 426# endif 427 ) { 428# ifdef SECURE 429 (void) setgid(getgid()); 430 (void) setuid(getuid()); /* Ron Wessels */ 431# endif 432 } else { 433 /* non-default data files is a sign that scores may not be 434 * compatible, or perhaps that a binary not fitting this 435 * system's layout is being used. 436 */ 437# ifdef VAR_PLAYGROUND 438 int len = strlen(VAR_PLAYGROUND); 439 440 fqn_prefix[SCOREPREFIX] = (char *)alloc(len+2); 441 Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND); 442 if (fqn_prefix[SCOREPREFIX][len-1] != '/') { 443 fqn_prefix[SCOREPREFIX][len] = '/'; 444 fqn_prefix[SCOREPREFIX][len+1] = '\0'; 445 } 446# endif 447 } 448 449# ifdef HACKDIR 450 if (dir == (const char *)0) 451 dir = HACKDIR; 452# endif 453 454 if (dir && chdir(dir) < 0) { 455 perror(dir); 456 error("Cannot chdir to %s.", dir); 457 } 458 459 /* warn the player if we can't write the record file */ 460 /* perhaps we should also test whether . is writable */ 461 /* unfortunately the access system-call is worthless */ 462 if (wr) { 463# ifdef VAR_PLAYGROUND 464 fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX]; 465 fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX]; 466 fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX]; 467 fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX]; 468 fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX]; 469# endif 470 check_recordfile(dir); 471 } 472} 473#endif /* CHDIR */ 474 475static boolean 476whoami() { 477 /* 478 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS 479 * 2. Use $USER or $LOGNAME (if 1. fails) 480 * 3. Use getlogin() (if 2. fails) 481 * The resulting name is overridden by command line options. 482 * If everything fails, or if the resulting name is some generic 483 * account like "games", "play", "player", "hack" then eventually 484 * we'll ask him. 485 * Note that we trust the user here; it is possible to play under 486 * somebody else's name. 487 */ 488 register char *s; 489 490 if (*plname) return FALSE; 491 if(/* !*plname && */ (s = nh_getenv("USER"))) 492 (void) strncpy(plname, s, sizeof(plname)-1); 493 if(!*plname && (s = nh_getenv("LOGNAME"))) 494 (void) strncpy(plname, s, sizeof(plname)-1); 495 if(!*plname && (s = getlogin())) 496 (void) strncpy(plname, s, sizeof(plname)-1); 497 return TRUE; 498} 499 500#ifdef PORT_HELP 501void 502port_help() 503{ 504 /* 505 * Display unix-specific help. Just show contents of the helpfile 506 * named by PORT_HELP. 507 */ 508 display_file(PORT_HELP, TRUE); 509} 510#endif 511 512static void 513wd_message() 514{ 515#ifdef WIZARD 516 if (wiz_error_flag) { 517 pline("Only user \"%s\" may access debug (wizard) mode.", 518# ifndef KR1ED 519 WIZARD); 520# else 521 WIZARD_NAME); 522# endif 523 pline("Entering discovery mode instead."); 524 } else 525#endif 526 if (discover) 527 You("are in non-scoring discovery mode."); 528} 529 530/* 531 * Add a slash to any name not ending in /. There must 532 * be room for the / 533 */ 534void 535append_slash(name) 536char *name; 537{ 538 char *ptr; 539 540 if (!*name) 541 return; 542 ptr = name + (strlen(name) - 1); 543 if (*ptr != '/') { 544 *++ptr = '/'; 545 *++ptr = '\0'; 546 } 547 return; 548} 549 550/*unixmain.c*/ 551