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