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 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
process_options(argc,argv)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
chdirx(dir,wr)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
whoami()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
port_help()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
wd_message()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*/
495