Source:NetHack 3.0.0/unixmain.c

From NetHackWiki
Jump to navigation Jump to search

Below is the full text to unixmain.c from the source code of NetHack 3.0.0. To link to a particular line, write [[NetHack 3.0.0/unixmain.c#line123]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

1.    /*	SCCS Id: @(#)unixmain.c	3.0	89/01/13
2.    /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3.    /* NetHack may be freely redistributed.  See license for details. */
4.    /* main.c - (Unix) version */
5.    
6.    #include <signal.h>
7.    #include <pwd.h>
8.    
9.    #include "hack.h"
10.   
11.   int hackpid = 0;				/* current pid */
12.   int locknum = 0;				/* max num of players */
13.   #ifdef DEF_PAGER
14.   char *catmore = 0;				/* default pager */
15.   #endif
16.   char SAVEF[PL_NSIZ + 11] = "save/";	/* save/99999player */
17.   char *hname = 0;		/* name of the game (argv[0] of call) */
18.   char obuf[BUFSIZ];	/* BUFSIZ is defined in stdio.h */
19.   
20.   int (*occupation)() = DUMMY;
21.   int (*afternmv)() = DUMMY;
22.   #ifdef CHDIR
23.   static void chdirx();
24.   #endif /* CHDIR */
25.   static void whoami(), newgame();
26.   
27.   main(argc,argv)
28.   int argc;
29.   char *argv[];
30.   {
31.   	struct passwd *pw;
32.   	extern struct passwd *getpwuid();
33.   	extern int x_maze_max, y_maze_max;
34.   	register int fd;
35.   #ifdef CHDIR
36.   	register char *dir;
37.   #endif
38.   #ifdef COMPRESS
39.   	char	cmd[80], old[80];
40.   #endif
41.   	hname = argv[0];
42.   	hackpid = getpid();
43.   	(void) umask(0);
44.   
45.   #ifdef CHDIR			/* otherwise no chdir() */
46.   	/*
47.   	 * See if we must change directory to the playground.
48.   	 * (Perhaps hack runs suid and playground is inaccessible
49.   	 *  for the player.)
50.   	 * The environment variable HACKDIR is overridden by a
51.   	 *  -d command line option (must be the first option given)
52.   	 */
53.   
54.   	dir = getenv("HACKDIR");
55.   	if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
56.   		argc--;
57.   		argv++;
58.   		dir = argv[0]+2;
59.   		if(*dir == '=' || *dir == ':') dir++;
60.   		if(!*dir && argc > 1) {
61.   			argc--;
62.   			argv++;
63.   			dir = argv[0];
64.   		}
65.   		if(!*dir)
66.   		    error("Flag -d must be followed by a directory name.");
67.   	}
68.   #endif /* CHDIR /**/
69.   	/* Set the default values of the presentation characters */
70.   	(void) memcpy((char *) &showsyms, 
71.   		(char *) &defsyms, sizeof(struct symbols));
72.   	initoptions();
73.   	whoami();
74.   	/*
75.   	 * Now we know the directory containing 'record' and
76.   	 * may do a prscore().
77.   	 */
78.   	if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
79.   #ifdef CHDIR
80.   		chdirx(dir,0);
81.   #endif
82.   		prscore(argc, argv);
83.   		exit(0);
84.   	}
85.   
86.   	/*
87.   	 * It seems he really wants to play.
88.   	 * Remember tty modes, to be restored on exit.
89.   	 */
90.   	gettty();
91.   	setbuf(stdout,obuf);
92.   	setrandom();
93.   	startup();
94.   	cls();
95.   	u.uhp = 1;	/* prevent RIP on early quits */
96.   	u.ux = FAR;	/* prevent nscr() */
97.   	(void) signal(SIGHUP, (SIG_RET_TYPE) hangup);
98.   
99.   	/*
100.  	 * Find the creation date of this game,
101.  	 * so as to avoid restoring outdated savefiles.
102.  	 */
103.  	gethdate(hname);
104.  
105.  	/*
106.  	 * We cannot do chdir earlier, otherwise gethdate will fail.
107.  	 */
108.  #ifdef CHDIR
109.  	chdirx(dir,1);
110.  #endif
111.  
112.  	/*
113.  	 * Process options.
114.  	 */
115.  	while(argc > 1 && argv[1][0] == '-'){
116.  		argv++;
117.  		argc--;
118.  		switch(argv[0][1]){
119.  #if defined(WIZARD) || defined(EXPLORE_MODE)
120.  		case 'D':
121.  		case 'X':
122.  			pw = getpwuid(getuid());
123.  # ifdef WIZARD
124.  			if(!strcmp(pw->pw_name, WIZARD))
125.  				wizard = TRUE;
126.  # endif
127.  # if defined(WIZARD) && defined(EXPLORE_MODE)
128.  			else
129.  # endif
130.  # ifdef EXPLORE_MODE
131.  				discover = TRUE;
132.  # endif
133.  			break;
134.  #endif
135.  #ifdef NEWS
136.  		case 'n':
137.  			flags.nonews = TRUE;
138.  			break;
139.  #endif
140.  		case 'u':
141.  			if(argv[0][2])
142.  			  (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
143.  			else if(argc > 1) {
144.  			  argc--;
145.  			  argv++;
146.  			  (void) strncpy(plname, argv[0], sizeof(plname)-1);
147.  			} else
148.  				Printf("Player name expected after -u\n");
149.  			break;
150.  		default:
151.  			/* allow -T for Tourist, etc. */
152.  			(void) strncpy(pl_character, argv[0]+1,
153.  				sizeof(pl_character)-1);
154.  
155.  			/* Printf("Unknown option: %s\n", *argv); */
156.  		}
157.  	}
158.  
159.  	if(argc > 1)
160.  		locknum = atoi(argv[1]);
161.  #ifdef MAX_NR_OF_PLAYERS
162.  	if(!locknum || locknum > MAX_NR_OF_PLAYERS)
163.  		locknum = MAX_NR_OF_PLAYERS;
164.  #endif
165.  #ifdef DEF_PAGER
166.  	if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
167.  		catmore = DEF_PAGER;
168.  #endif
169.  #ifdef MAIL
170.  	getmailstatus();
171.  #endif
172.  #ifdef WIZARD
173.  	if(wizard) Strcpy(plname, "wizard"); else
174.  #endif
175.  	if(!*plname || !strncmp(plname, "player", 4)
176.  		    || !strncmp(plname, "games", 4))
177.  		askname();
178.  	plnamesuffix();		/* strip suffix from name; calls askname() */
179.  				/* again if suffix was whole name */
180.  				/* accepts any suffix */
181.  #ifdef WIZARD
182.  	if(!wizard) {
183.  #endif
184.  		/*
185.  		 * check for multiple games under the same name
186.  		 * (if !locknum) or check max nr of players (otherwise)
187.  		 */
188.  		(void) signal(SIGQUIT,SIG_IGN);
189.  		(void) signal(SIGINT,SIG_IGN);
190.  		if(!locknum)
191.  			Strcpy(lock,plname);
192.  		getlock();	/* sets lock if locknum != 0 */
193.  #ifdef WIZARD
194.  	} else
195.  		Strcpy(lock,plname);
196.  #endif /* WIZARD /**/
197.  	setftty();
198.  
199.  	/* 
200.  	 * Initialisation of the boundaries of the mazes
201.  	 * Both boundaries have to be even.
202.  	 */
203.  	 
204.  	x_maze_max = COLNO-1;
205.  	if (x_maze_max % 2) 
206.  		x_maze_max--;
207.  	y_maze_max = ROWNO-1;
208.  	if (y_maze_max % 2) 
209.  		y_maze_max--;
210.  
211.  	/* initialize static monster strength array */
212.  	init_monstr();
213.  
214.  	Sprintf(SAVEF, "save/%d%s", getuid(), plname);
215.  	regularize(SAVEF+5);		/* avoid . or / in name */
216.  #ifdef COMPRESS
217.  	Strcpy(old,SAVEF);
218.  	Strcat(SAVEF,".Z");
219.  	if((fd = open(SAVEF,0)) >= 0) {
220.   	    (void) close(fd);
221.  	    Strcpy(cmd, COMPRESS);
222.  	    Strcat(cmd, " -d ");	/* uncompress */
223.  # ifdef COMPRESS_OPTIONS
224.  	    Strcat(cmd, COMPRESS_OPTIONS);
225.  	    Strcat(cmd, " ");
226.  # endif
227.  	    Strcat(cmd,SAVEF);
228.  	    (void) system(cmd);
229.  	}
230.  	Strcpy(SAVEF,old);
231.  #endif
232.  	if((fd = open(SAVEF,0)) >= 0 &&
233.  	   (uptodate(fd) || unlink(SAVEF) == 666)) {
234.  		(void) signal(SIGINT, (SIG_RET_TYPE) done1);
235.  		pline("Restoring old save file...");
236.  		(void) fflush(stdout);
237.  		if(!dorecover(fd))
238.  			goto not_recovered;
239.  		pline("Hello %s, welcome to NetHack!", plname);
240.  		/* get shopkeeper set properly if restore is in shop */
241.  		(void) inshop();
242.  #ifdef EXPLORE_MODE
243.  		if (discover) {
244.  			You("are in non-scoring discovery mode.");
245.  			pline("Do you want to keep the save file? ");
246.  			if(yn() == 'n')
247.  				(void) unlink(SAVEF);
248.  		}
249.  #endif
250.  		flags.move = 0;
251.  	} else {
252.  not_recovered:
253.  		newgame();
254.  		/* give welcome message before pickup messages */
255.  		pline("Hello %s, welcome to NetHack!", plname);
256.  #ifdef EXPLORE_MODE
257.  		if (discover)
258.  			You("are in non-scoring discovery mode.");
259.  #endif
260.  		flags.move = 0;
261.  		set_wear();
262.  		pickup(1);
263.  		read_engr_at(u.ux,u.uy);
264.  	}
265.  
266.  	flags.moonphase = phase_of_the_moon();
267.  	if(flags.moonphase == FULL_MOON) {
268.  		You("are lucky!  Full moon tonight.");
269.  		if(!u.uluck) change_luck(1);
270.  	} else if(flags.moonphase == NEW_MOON) {
271.  		pline("Be careful!  New moon tonight.");
272.  	}
273.  
274.  	initrack();
275.  
276.  	for(;;) {
277.  		if(flags.move) {	/* actual time passed */
278.  
279.  #ifdef SOUNDS
280.  			dosounds();
281.  #endif
282.  			settrack();
283.  
284.  			if(moves%2 == 0 ||
285.  			  (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
286.  				movemon();
287.  #ifdef HARD
288.  				if(!rn2(u.udemigod?25:(dlevel>30)?50:70))
289.  #else
290.  				if(!rn2(70))
291.  #endif
292.  				    (void) makemon((struct permonst *)0, 0, 0);
293.  			}
294.  			if(Glib) glibr();
295.  			timeout();
296.  			++moves;
297.  #ifdef THEOLOGY
298.  			if (u.ublesscnt)  u.ublesscnt--;
299.  #endif
300.  			if(flags.time) flags.botl = 1;
301.  #ifdef POLYSELF
302.  			if(u.mtimedone)
303.  			    if(u.mh < 1) rehumanize();
304.  			else
305.  #endif
306.  			    if(u.uhp < 1) {
307.  				You("die...");
308.  				done("died");
309.  			    }

Lines 301 through 309 are an example of a "dangling else", and NetHack 3.0 is rife with them. This particular one is likely a bug. The "else" at line 304 pairs with the "if" at line 303, and not the one at line 302 as the indentation seems to imply. This is an example of why you should use braces when in doubt, and pay attention to your compiler warnings.

In NetHack 3.1.0, the corresponding section is allmain.c, line 96, and the first part is properly braced.

310.  #ifdef POLYSELF
311.  			if (u.mtimedone) {
312.  			    if (u.mh < u.mhmax) {
313.  				if (Regeneration || !(moves%20)) {
314.  					flags.botl = 1;
315.  					u.mh++;
316.  				}
317.  			    }
318.  			}
319.  #endif
320.  			if(u.uhp < u.uhpmax) {
321.  				if(u.ulevel > 9) {
322.  				    int heal;
323.  
324.  				    if(HRegeneration || !(moves%3)) {
325.  					flags.botl = 1;
326.  					if (ACURR(A_CON) <= 12) heal = 1;
327.  					else heal = rnd((int) ACURR(A_CON)-12);
328.  					if (heal > u.ulevel-9) heal = u.ulevel-9;
329.  					u.uhp += heal;
330.  					if(u.uhp > u.uhpmax)
331.  					    u.uhp = u.uhpmax;
332.  				    }
333.  				} else if(HRegeneration ||
334.  				      (!(moves%((MAXULEV+12)/(u.ulevel+2)+1)))) {
335.  					flags.botl = 1;
336.  					u.uhp++;
337.  				}
338.  			}
339.  #ifdef SPELLS
340.  			if ((u.uen<u.uenmax) && (!(moves%(19-ACURR(A_INT)/2)))) {
341.  				u.uen += rn2((int)ACURR(A_WIS)/5 + 1) + 1;
342.  				if (u.uen > u.uenmax)  u.uen = u.uenmax;
343.  				flags.botl = 1;
344.  			}
345.  #endif
346.  			if(Teleportation && !rn2(85)) tele();
347.  #ifdef POLYSELF
348.  			if(Polymorph && !rn2(100)) polyself();
349.  			if(u.ulycn >= 0 && !rn2(80 - (20 * night())))
350.  				you_were();
351.  #endif
352.  			if(Searching && multi >= 0) (void) dosearch0(1);
353.  			hatch_eggs();
354.  			gethungry();
355.  			invault();
356.  			amulet();
357.  #ifdef HARD
358.  			if (!rn2(40+(int)(ACURR(A_DEX)*3))) u_wipe_engr(rnd(3));
359.  			if (u.udemigod) {
360.  
361.  				if(u.udg_cnt) u.udg_cnt--;
362.  				if(!u.udg_cnt) {
363.  
364.  					intervene();
365.  					u.udg_cnt = rn1(200, 50);
366.  				}
367.  			}
368.  #endif
369.  			restore_attrib();
370.  		}
371.  		if(multi < 0) {
372.  			if(!++multi){
373.  				pline(nomovemsg ? nomovemsg :
374.  					"You can move again.");
375.  				nomovemsg = 0;
376.  				if(afternmv) (*afternmv)();
377.  				afternmv = 0;
378.  			}
379.  		}
380.  
381.  		find_ac();
382.  		if(!flags.mv || Blind)
383.  		{
384.  			seeobjs();
385.  			seemons();
386.  			seeglds();
387.  			nscr();
388.  		}
389.  		if(flags.botl || flags.botlx) bot();
390.  
391.  		flags.move = 1;
392.  
393.  		if(multi >= 0 && occupation) {
394.  
395.  			if(monster_nearby())
396.  				stop_occupation();
397.  			else if ((*occupation)() == 0)
398.  				occupation = 0;
399.  			continue;
400.  		}
401.  
402.  		if((u.uhave_amulet || Clairvoyant) && 
403.  #ifdef ENDGAME
404.  			dlevel != ENDLEVEL &&
405.  #endif
406.  			!(moves%15) && !rn2(2)) do_vicinity_map();
407.  
408.  		u.umoved = FALSE;
409.  		if(multi > 0) {
410.  			lookaround();
411.  			if(!multi) {	/* lookaround may clear multi */
412.  				flags.move = 0;
413.  				continue;
414.  			}
415.  			if(flags.mv) {
416.  				if(multi < COLNO && !--multi)
417.  					flags.mv = flags.run = 0;
418.  				domove();
419.  			} else {
420.  				--multi;
421.  				rhack(save_cm);
422.  			}
423.  		} else if(multi == 0) {
424.  #ifdef MAIL
425.  			ckmailstatus();
426.  #endif
427.  			rhack(NULL);
428.  		}
429.  		if(multi && multi%7 == 0)
430.  			(void) fflush(stdout);
431.  	}
432.  }
433.  
434.  void
435.  glo(foo)
436.  register int foo;
437.  {
438.  	/* construct the string  xlock.n  */
439.  	register char *tf;
440.  
441.  	tf = lock;
442.  	while(*tf && *tf != '.') tf++;
443.  	Sprintf(tf, ".%d", foo);
444.  }
445.  
446.  /*
447.   * plname is filled either by an option (-u Player  or  -uPlayer) or
448.   * explicitly (by being the wizard) or by askname.
449.   * It may still contain a suffix denoting pl_character.
450.   */
451.  void
452.  askname(){
453.  register int c,ct;
454.  	Printf("\nWho are you? ");
455.  	(void) fflush(stdout);
456.  	ct = 0;
457.  	while((c = Getchar()) != '\n'){
458.  		if(c == EOF) error("End of input\n");
459.  		/* some people get confused when their erase char is not ^H */
460.  		if(c == '\010') {
461.  			if(ct) ct--;
462.  			continue;
463.  		}
464.  		if(c != '-')
465.  		if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
466.  		if(ct < sizeof(plname)-1) plname[ct++] = c;
467.  	}
468.  	plname[ct] = 0;
469.  	if(ct == 0) askname();
470.  }
471.  
472.  /*VARARGS1*/
473.  void
474.  impossible(s,x1,x2)
475.  register char *s, *x1, *x2;
476.  {
477.  	pline(s,x1,x2);
478.  	pline("Program in disorder - perhaps you'd better Quit.");
479.  }
480.  
481.  #ifdef CHDIR
482.  static void
483.  chdirx(dir, wr)
484.  char *dir;
485.  boolean wr;
486.  {
487.  
488.  # ifdef SECURE
489.  	if(dir					/* User specified directory? */
490.  #  ifdef HACKDIR
491.  	       && strcmp(dir, HACKDIR)		/* and not the default? */
492.  #  endif
493.  		) {
494.  		(void) setgid(getgid());
495.  		(void) setuid(getuid());		/* Ron Wessels */
496.  	}
497.  # endif
498.  
499.  # ifdef HACKDIR
500.  	if(dir == NULL)
501.  		dir = HACKDIR;
502.  # endif
503.  
504.  	if(dir && chdir(dir) < 0) {
505.  		perror(dir);
506.  		error("Cannot chdir to %s.", dir);
507.  	}
508.  
509.  	/* warn the player if he cannot write the record file */
510.  	/* perhaps we should also test whether . is writable */
511.  	/* unfortunately the access systemcall is worthless */
512.  	if(wr) {
513.  	    register int fd;
514.  
515.  	    if(dir == NULL)
516.  		dir = ".";
517.  	    if((fd = open(RECORD, 2)) < 0) {
518.  		Printf("Warning: cannot write %s/%s", dir, RECORD);
519.  		getret();
520.  	    } else
521.  		(void) close(fd);
522.  	}
523.  }
524.  #endif /* CHDIR /**/
525.  
526.  void
527.  stop_occupation()
528.  {
529.  	if(occupation) {
530.  		You("stop %s.", occtxt);
531.  		occupation = 0;
532.  #ifdef REDO
533.  		multi = 0;
534.  		pushch(0);		
535.  #endif
536.  	}
537.  }
538.  
539.  static void
540.  whoami() {
541.  	/*
542.  	 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
543.  	 *			2. Use $USER or $LOGNAME	(if 1. fails)
544.  	 *			3. Use getlogin()		(if 2. fails)
545.  	 * The resulting name is overridden by command line options.
546.  	 * If everything fails, or if the resulting name is some generic
547.  	 * account like "games", "play", "player", "hack" then eventually
548.  	 * we'll ask him.
549.  	 * Note that we trust him here; it is possible to play under
550.  	 * somebody else's name.
551.  	 */
552.  	register char *s;
553.  
554.  	if(!*plname && (s = getenv("USER")))
555.  		(void) strncpy(plname, s, sizeof(plname)-1);
556.  	if(!*plname && (s = getenv("LOGNAME")))
557.  		(void) strncpy(plname, s, sizeof(plname)-1);
558.  	if(!*plname && (s = getlogin()))
559.  		(void) strncpy(plname, s, sizeof(plname)-1);
560.  }
561.  
562.  static void
563.  newgame() {
564.  	fobj = fcobj = invent = 0;
565.  	fmon = fallen_down = 0;
566.  	ftrap = 0;
567.  	fgold = 0;
568.  	flags.ident = 1;
569.  
570.  	init_objects();
571.  	u_init();
572.  
573.  	(void) signal(SIGINT, (SIG_RET_TYPE) done1);
574.  
575.  	mklev();
576.  	u.ux = xupstair;
577.  	u.uy = yupstair;
578.  	(void) inshop();
579.  
580.  	setsee();
581.  	flags.botlx = 1;
582.  
583.  	/* Move the monster from under you or else
584.  	 * makedog() will fail when it calls makemon().
585.  	 * 			- ucsfcgl!kneller
586.  	 */
587.  	if(levl[u.ux][u.uy].mmask) mnexto(m_at(u.ux, u.uy));
588.  
589.  	(void) makedog();
590.  	seemons();
591.  #ifdef NEWS
592.  	if(flags.nonews || !readnews())
593.  		/* after reading news we did docrt() already */
594.  #endif
595.  		docrt();
596.  
597.  	return;
598.  }