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