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