xref: /netbsd/games/hunt/hunt/hunt.c (revision bf9ec67e)
1 /*	$NetBSD: hunt.c,v 1.12 2001/02/05 00:40:45 christos Exp $	*/
2 /*
3  *  Hunt
4  *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
5  *  San Francisco, California
6  */
7 
8 #include <sys/cdefs.h>
9 #ifndef lint
10 __RCSID("$NetBSD: hunt.c,v 1.12 2001/02/05 00:40:45 christos Exp $");
11 #endif /* not lint */
12 
13 # include	<sys/param.h>
14 # include	<sys/stat.h>
15 # include	<sys/time.h>
16 # include	<ctype.h>
17 # include	<err.h>
18 # include	<errno.h>
19 # include	<curses.h>
20 # include	<signal.h>
21 # include	<stdlib.h>
22 # include	<string.h>
23 # if !defined(USE_CURSES) && defined(BSD_RELEASE) && BSD_RELEASE >= 44
24 # include	<termios.h>
25 static struct termios saved_tty;
26 # endif
27 # include	<unistd.h>
28 
29 # include	"hunt.h"
30 
31 /*
32  * Some old versions of curses don't have these defined
33  */
34 # if !defined(cbreak) && (!defined(BSD_RELEASE) || BSD_RELEASE < 44)
35 # define	cbreak()	crmode()
36 # endif
37 
38 # if !defined(USE_CURSES) || !defined(TERMINFO)
39 # define	beep()		(void) putchar(CTRL('G'))
40 # endif
41 # if !defined(USE_CURSES)
42 # undef		refresh
43 # define	refresh()	(void) fflush(stdout);
44 # endif
45 # ifdef USE_CURSES
46 # define	clear_eol()	clrtoeol()
47 # define	put_ch		addch
48 # define	put_str		addstr
49 # endif
50 
51 #if !defined(BSD_RELEASE) || BSD_RELEASE < 44
52 extern int	_putchar();
53 #endif
54 
55 FLAG	Last_player = FALSE;
56 # ifdef MONITOR
57 FLAG	Am_monitor = FALSE;
58 # endif
59 
60 char	Buf[BUFSIZ];
61 
62 int	Socket;
63 # ifdef INTERNET
64 char	*Sock_host;
65 char	*use_port;
66 FLAG	Query_driver = FALSE;
67 char	*Send_message = NULL;
68 FLAG	Show_scores = FALSE;
69 # endif
70 
71 SOCKET	Daemon;
72 # ifdef	INTERNET
73 # define	DAEMON_SIZE	(sizeof Daemon)
74 # else
75 # define	DAEMON_SIZE	(sizeof Daemon - 1)
76 # endif
77 
78 char	map_key[256];			/* what to map keys to */
79 FLAG	no_beep;
80 
81 static char	name[NAMELEN];
82 static char	team = ' ';
83 
84 static int	in_visual;
85 
86 extern int	cur_row, cur_col;
87 
88 void	dump_scores __P((SOCKET));
89 long	env_init __P((long));
90 void	fill_in_blanks __P((void));
91 void	leave __P((int, char *)) __attribute__((__noreturn__));
92 int	main __P((int, char *[]));
93 # ifdef INTERNET
94 SOCKET *list_drivers __P((void));
95 # endif
96 
97 extern int	Otto_mode;
98 /*
99  * main:
100  *	Main program for local process
101  */
102 int
103 main(ac, av)
104 	int	ac;
105 	char	**av;
106 {
107 	char		*term;
108 	int		c;
109 	long		enter_status;
110 
111 	enter_status = env_init((long) Q_CLOAK);
112 	while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != -1) {
113 		switch (c) {
114 		case 'l':	/* rsh compatibility */
115 		case 'n':
116 			(void) strncpy(name, optarg, NAMELEN);
117 			break;
118 		case 't':
119 			team = *optarg;
120 			if (!isdigit(team)) {
121 				warnx("Team names must be numeric");
122 				team = ' ';
123 			}
124 			break;
125 		case 'o':
126 # ifndef OTTO
127 			warnx("The -o flag is reserved for future use.");
128 			goto usage;
129 # else
130 			Otto_mode = TRUE;
131 			break;
132 # endif
133 		case 'm':
134 # ifdef MONITOR
135 			Am_monitor = TRUE;
136 # else
137 			warnx("The monitor was not compiled in.");
138 # endif
139 			break;
140 # ifdef INTERNET
141 		case 'S':
142 			Show_scores = TRUE;
143 			break;
144 		case 'q':	/* query whether hunt is running */
145 			Query_driver = TRUE;
146 			break;
147 		case 'w':
148 			Send_message = optarg;
149 			break;
150 		case 'h':
151 			Sock_host = optarg;
152 			break;
153 		case 'p':
154 			use_port = optarg;
155 			Test_port = atoi(use_port);
156 			break;
157 # else
158 		case 'S':
159 		case 'q':
160 		case 'w':
161 		case 'h':
162 		case 'p':
163 			wanrx("Need TCP/IP for S, q, w, h, and p options.");
164 			break;
165 # endif
166 		case 'c':
167 			enter_status = Q_CLOAK;
168 			break;
169 		case 'f':
170 # ifdef FLY
171 			enter_status = Q_FLY;
172 # else
173 			warnx("The flying code was not compiled in.");
174 # endif
175 			break;
176 		case 's':
177 			enter_status = Q_SCAN;
178 			break;
179 		case 'b':
180 			no_beep = !no_beep;
181 			break;
182 		default:
183 		usage:
184 			fputs(
185 "usage:\thunt [-qmcsfS] [-n name] [-t team] [-p port] [-w message] [host]\n",
186 			stderr);
187 			exit(1);
188 		}
189 	}
190 # ifdef INTERNET
191 	if (optind + 1 < ac)
192 		goto usage;
193 	else if (optind + 1 == ac)
194 		Sock_host = av[ac - 1];
195 # else
196 	if (optind > ac)
197 		goto usage;
198 # endif
199 
200 # ifdef INTERNET
201 	if (Show_scores) {
202 		SOCKET	*hosts;
203 
204 		for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1)
205 			dump_scores(*hosts);
206 		exit(0);
207 	}
208 	if (Query_driver) {
209 		SOCKET	*hosts;
210 
211 		for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1) {
212 			struct	hostent	*hp;
213 			int	num_players;
214 
215 			hp = gethostbyaddr((char *) &hosts->sin_addr,
216 					sizeof hosts->sin_addr, AF_INET);
217 			num_players = ntohs(hosts->sin_port);
218 			printf("%d player%s hunting on %s!\n",
219 				num_players, (num_players == 1) ? "" : "s",
220 				hp != NULL ? hp->h_name :
221 				inet_ntoa(hosts->sin_addr));
222 		}
223 		exit(0);
224 	}
225 # endif
226 # ifdef OTTO
227 	if (Otto_mode)
228 		(void) strncpy(name, "otto", NAMELEN);
229 	else
230 # endif
231 	fill_in_blanks();
232 
233 	(void) fflush(stdout);
234 	if (!isatty(0) || (term = getenv("TERM")) == NULL)
235 		errx(1, "no terminal type");
236 # ifdef USE_CURSES
237 	initscr();
238 	(void) noecho();
239 	(void) cbreak();
240 # else /* !USE_CURSES */
241 # if !defined(BSD_RELEASE) || BSD_RELEASE < 44
242 	_tty_ch = 0;
243 # endif
244 	gettmode();
245 	(void) setterm(term);
246 	(void) noecho();
247 	(void) cbreak();
248 # if defined(BSD_RELEASE) && BSD_RELEASE >= 44
249 	tcgetattr(0, &saved_tty);
250 # endif
251 	_puts(TI);
252 	_puts(VS);
253 # endif /* !USE_CURSES */
254 	in_visual = TRUE;
255 	if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH)
256 		leave(1, "Need a larger window");
257 	clear_the_screen();
258 	(void) signal(SIGINT, intr);
259 	(void) signal(SIGTERM, sigterm);
260 	(void) signal(SIGEMT, sigemt);
261 	(void) signal(SIGPIPE, SIG_IGN);
262 #if !defined(USE_CURSES) && defined(SIGTSTP)
263 	(void) signal(SIGTSTP, tstp);
264 #endif
265 
266 	for (;;) {
267 # ifdef	INTERNET
268 		find_driver(TRUE);
269 
270 		if (Daemon.sin_port == 0)
271 			leave(1, "Game not found, try again");
272 
273 	jump_in:
274 		do {
275 			int	option;
276 
277 			Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
278 			if (Socket < 0)
279 				err(1, "socket");
280 			option = 1;
281 			if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
282 			    &option, sizeof option) < 0)
283 				warn("setsockopt loopback");
284 			errno = 0;
285 			if (connect(Socket, (struct sockaddr *) &Daemon,
286 			    DAEMON_SIZE) < 0) {
287 				if (errno != ECONNREFUSED) {
288 					warn("connect");
289 					leave(1, "connect");
290 				}
291 			}
292 			else
293 				break;
294 			sleep(1);
295 		} while (close(Socket) == 0);
296 # else /* !INTERNET */
297 		/*
298 		 * set up a socket
299 		 */
300 
301 		if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0)
302 			err(1, "socket");
303 
304 		/*
305 		 * attempt to connect the socket to a name; if it fails that
306 		 * usually means that the driver isn't running, so we start
307 		 * up the driver.
308 		 */
309 
310 		Daemon.sun_family = SOCK_FAMILY;
311 		(void) strcpy(Daemon.sun_path, Sock_name);
312 		if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) {
313 			if (errno != ENOENT) {
314 				warn("connect");
315 				leave(1, "connect2");
316 			}
317 			start_driver();
318 
319 			do {
320 				(void) close(Socket);
321 				if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM,
322 				    0)) < 0)
323 					err(1, "socket");
324 				sleep(2);
325 			} while (connect(Socket, &Daemon, DAEMON_SIZE) < 0);
326 		}
327 # endif
328 
329 		do_connect(name, team, enter_status);
330 # ifdef INTERNET
331 		if (Send_message != NULL) {
332 			do_message();
333 			if (enter_status == Q_MESSAGE)
334 				break;
335 			Send_message = NULL;
336 			/* don't continue as that will call find_driver */
337 			goto jump_in;
338 		}
339 # endif
340 		playit();
341 		if ((enter_status = quit(enter_status)) == Q_QUIT)
342 			break;
343 	}
344 	leave(0, (char *) NULL);
345 	/* NOTREACHED */
346 	return(0);
347 }
348 
349 # ifdef INTERNET
350 # ifdef BROADCAST
351 int
352 broadcast_vec(s, vector)
353 	int			s;		/* socket */
354 	struct	sockaddr	**vector;
355 {
356 	char			if_buf[BUFSIZ];
357 	struct	ifconf		ifc;
358 	struct	ifreq		*ifr;
359 	unsigned int		n;
360 	int			vec_cnt;
361 
362 	*vector = NULL;
363 	ifc.ifc_len = sizeof if_buf;
364 	ifc.ifc_buf = if_buf;
365 	if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
366 		return 0;
367 	vec_cnt = 0;
368 	n = ifc.ifc_len / sizeof (struct ifreq);
369 	*vector = (struct sockaddr *) malloc(n * sizeof (struct sockaddr));
370 	for (ifr = ifc.ifc_req; n != 0; n--, ifr++)
371 		if (ioctl(s, SIOCGIFBRDADDR, ifr) >= 0)
372 			memcpy(&(*vector)[vec_cnt++], &ifr->ifr_addr,
373 				sizeof (struct sockaddr));
374 	return vec_cnt;
375 }
376 # endif
377 
378 SOCKET	*
379 list_drivers()
380 {
381 	int			option;
382 	u_short			msg;
383 	u_short			port_num;
384 	static SOCKET		test;
385 	int			test_socket;
386 	int			namelen;
387 	char			local_name[MAXHOSTNAMELEN + 1];
388 	static int		initial = TRUE;
389 	static struct in_addr	local_address;
390 	struct hostent		*hp;
391 # ifdef BROADCAST
392 	static	int		brdc;
393 	static	SOCKET		*brdv;
394 # else
395 	u_long			local_net;
396 # endif
397 	int			i;
398 	static	SOCKET		*listv;
399 	static	unsigned int	listmax;
400 	unsigned int		listc;
401 	fd_set			mask;
402 	struct timeval		wait;
403 
404 	if (initial) {			/* do one time initialization */
405 # ifndef BROADCAST
406 		sethostent(1);		/* don't bother to close host file */
407 # endif
408 		if (gethostname(local_name, sizeof local_name) < 0) {
409 			leave(1, "Sorry, I have no name.");
410 			/* NOTREACHED */
411 		}
412 		local_name[sizeof(local_name) - 1] = '\0';
413 		if ((hp = gethostbyname(local_name)) == NULL) {
414 			leave(1, "Can't find myself.");
415 			/* NOTREACHED */
416 		}
417 		local_address = * ((struct in_addr *) hp->h_addr);
418 
419 		listmax = 20;
420 		listv = (SOCKET *) malloc(listmax * sizeof (SOCKET));
421 	} else if (Sock_host != NULL)
422 		return listv;		/* address already valid */
423 
424 	test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
425 	if (test_socket < 0) {
426 		warn("socket");
427 		leave(1, "socket system call failed");
428 		/* NOTREACHED */
429 	}
430 	test.sin_family = SOCK_FAMILY;
431 	test.sin_port = htons(Test_port);
432 	listc = 0;
433 
434 	if (Sock_host != NULL) {	/* explicit host given */
435 		if ((hp = gethostbyname(Sock_host)) == NULL) {
436 			leave(1, "Unknown host");
437 			/* NOTREACHED */
438 		}
439 		test.sin_addr = *((struct in_addr *) hp->h_addr);
440 		goto test_one_host;
441 	}
442 
443 	if (!initial) {
444 		/* favor host of previous session by broadcasting to it first */
445 		test.sin_addr = Daemon.sin_addr;
446 		msg = htons(C_PLAYER);		/* Must be playing! */
447 		(void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
448 		    (struct sockaddr *) &test, DAEMON_SIZE);
449 	}
450 
451 # ifdef BROADCAST
452 	if (initial)
453 		brdc = broadcast_vec(test_socket, (struct sockaddr **) &brdv);
454 
455 	if (brdc <= 0) {
456 		initial = FALSE;
457 		test.sin_addr = local_address;
458 		goto test_one_host;
459 	}
460 
461 # ifdef SO_BROADCAST
462 	/* Sun's will broadcast even though this option can't be set */
463 	option = 1;
464 	if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST,
465 	    &option, sizeof option) < 0) {
466 		warn("setsockopt broadcast");
467 		leave(1, "setsockopt broadcast");
468 		/* NOTREACHED */
469 	}
470 # endif
471 
472 	/* send broadcast packets on all interfaces */
473 	msg = htons(C_TESTMSG());
474 	for (i = 0; i < brdc; i++) {
475 		test.sin_addr = brdv[i].sin_addr;
476 		if (sendto(test_socket, (char *) &msg, sizeof msg, 0,
477 		    (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
478 			warn("sendto");
479 			leave(1, "sendto");
480 			/* NOTREACHED */
481 		}
482 	}
483 # else /* !BROADCAST */
484 	/* loop thru all hosts on local net and send msg to them. */
485 	msg = htons(C_TESTMSG());
486 	local_net = inet_netof(local_address);
487 	sethostent(0);		/* rewind host file */
488 	while (hp = gethostent()) {
489 		if (local_net == inet_netof(* ((struct in_addr *) hp->h_addr))){
490 			test.sin_addr = * ((struct in_addr *) hp->h_addr);
491 			(void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
492 			    (struct sockaddr *) &test, DAEMON_SIZE);
493 		}
494 	}
495 # endif
496 
497 get_response:
498 	namelen = DAEMON_SIZE;
499 	errno = 0;
500 	wait.tv_sec = 1;
501 	wait.tv_usec = 0;
502 	for (;;) {
503 		if (listc + 1 >= listmax) {
504 			listmax += 20;
505 			listv = (SOCKET *) realloc((char *) listv,
506 						listmax * sizeof(SOCKET));
507 		}
508 
509 		FD_ZERO(&mask);
510 		FD_SET(test_socket, &mask);
511 		if (select(test_socket + 1, &mask, NULL, NULL, &wait) == 1 &&
512 		    recvfrom(test_socket, (char *) &port_num, sizeof(port_num),
513 		    0, (struct sockaddr *) &listv[listc], &namelen) > 0) {
514 			/*
515 			 * Note that we do *not* convert from network to host
516 			 * order since the port number *should* be in network
517 			 * order:
518 			 */
519 			for (i = 0; i < listc; i += 1)
520 				if (listv[listc].sin_addr.s_addr
521 				== listv[i].sin_addr.s_addr)
522 					break;
523 			if (i == listc)
524 				listv[listc++].sin_port = port_num;
525 			continue;
526 		}
527 
528 		if (errno != 0 && errno != EINTR) {
529 			warn("select/recvfrom");
530 			leave(1, "select/recvfrom");
531 			/* NOTREACHED */
532 		}
533 
534 		/* terminate list with local address */
535 		listv[listc].sin_family = SOCK_FAMILY;
536 		listv[listc].sin_addr = local_address;
537 		listv[listc].sin_port = htons(0);
538 
539 		(void) close(test_socket);
540 		initial = FALSE;
541 		return listv;
542 	}
543 
544 test_one_host:
545 	msg = htons(C_TESTMSG());
546 	(void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
547 	    (struct sockaddr *) &test, DAEMON_SIZE);
548 	goto get_response;
549 }
550 
551 void
552 find_driver(do_startup)
553 	FLAG	do_startup;
554 {
555 	SOCKET	*hosts;
556 
557 	hosts = list_drivers();
558 	if (hosts[0].sin_port != htons(0)) {
559 		int	i, c;
560 
561 		if (hosts[1].sin_port == htons(0)) {
562 			Daemon = hosts[0];
563 			return;
564 		}
565 		/* go thru list and return host that matches daemon */
566 		clear_the_screen();
567 # ifdef USE_CURSES
568 		move(1, 0);
569 # else
570 		mvcur(cur_row, cur_col, 1, 0);
571 		cur_row = 1;
572 		cur_col = 0;
573 # endif
574 		put_str("Pick one:");
575 		for (i = 0; i < HEIGHT - 4 && hosts[i].sin_port != htons(0);
576 								i += 1) {
577 			struct	hostent	*hp;
578 			char	buf[80];
579 
580 # ifdef USE_CURSES
581 			move(3 + i, 0);
582 # else
583 			mvcur(cur_row, cur_col, 3 + i, 0);
584 			cur_row = 3 + i;
585 			cur_col = 0;
586 # endif
587 			hp = gethostbyaddr((char *) &hosts[i].sin_addr,
588 					sizeof hosts[i].sin_addr, AF_INET);
589 			(void) sprintf(buf, "%8c    %.64s", 'a' + i,
590 				hp != NULL ? hp->h_name
591 				: inet_ntoa(hosts->sin_addr));
592 			put_str(buf);
593 		}
594 # ifdef USE_CURSES
595 		move(4 + i, 0);
596 # else
597 		mvcur(cur_row, cur_col, 4 + i, 0);
598 		cur_row = 4 + i;
599 		cur_col = 0;
600 # endif
601 		put_str("Enter letter: ");
602 		refresh();
603 		while (!islower(c = getchar()) || (c -= 'a') >= i) {
604 			beep();
605 			refresh();
606 		}
607 		Daemon = hosts[c];
608 		clear_the_screen();
609 		return;
610 	}
611 	if (!do_startup)
612 		return;
613 
614 	start_driver();
615 	sleep(2);
616 	find_driver(FALSE);
617 }
618 
619 void
620 dump_scores(host)
621 	SOCKET	host;
622 {
623 	struct	hostent	*hp;
624 	int	s;
625 	char	buf[BUFSIZ];
626 	int	cnt;
627 
628 	hp = gethostbyaddr((char *) &host.sin_addr, sizeof host.sin_addr,
629 								AF_INET);
630 	printf("\n%s:\n", hp != NULL ? hp->h_name : inet_ntoa(host.sin_addr));
631 	fflush(stdout);
632 
633 	s = socket(SOCK_FAMILY, SOCK_STREAM, 0);
634 	if (s < 0)
635 		err(1, "socket");
636 	if (connect(s, (struct sockaddr *) &host, sizeof host) < 0)
637 		err(1, "connect");
638 	while ((cnt = read(s, buf, BUFSIZ)) > 0)
639 		write(fileno(stdout), buf, cnt);
640 	(void) close(s);
641 }
642 
643 # endif
644 
645 void
646 start_driver()
647 {
648 	int	procid;
649 
650 # ifdef MONITOR
651 	if (Am_monitor) {
652 		leave(1, "No one playing.");
653 		/* NOTREACHED */
654 	}
655 # endif
656 
657 # ifdef INTERNET
658 	if (Sock_host != NULL) {
659 		sleep(3);
660 		return;
661 	}
662 # endif
663 
664 # ifdef USE_CURSES
665 	move(HEIGHT, 0);
666 # else
667 	mvcur(cur_row, cur_col, HEIGHT, 0);
668 	cur_row = HEIGHT;
669 	cur_col = 0;
670 # endif
671 	put_str("Starting...");
672 	refresh();
673 	procid = fork();
674 	if (procid == -1) {
675 		warn("fork");
676 		leave(1, "fork failed.");
677 	}
678 	if (procid == 0) {
679 		(void) signal(SIGINT, SIG_IGN);
680 # ifndef INTERNET
681 		(void) close(Socket);
682 # else
683 		if (use_port == NULL)
684 # endif
685 			execl(Driver, "HUNT", (char *) NULL);
686 # ifdef INTERNET
687 		else
688 			execl(Driver, "HUNT", "-p", use_port, (char *) NULL);
689 # endif
690 		/* only get here if exec failed */
691 		(void) kill(getppid(), SIGEMT);	/* tell mom */
692 		_exit(1);
693 	}
694 # ifdef USE_CURSES
695 	move(HEIGHT, 0);
696 # else
697 	mvcur(cur_row, cur_col, HEIGHT, 0);
698 	cur_row = HEIGHT;
699 	cur_col = 0;
700 # endif
701 	put_str("Connecting...");
702 	refresh();
703 }
704 
705 /*
706  * bad_con:
707  *	We had a bad connection.  For the moment we assume that this
708  *	means the game is full.
709  */
710 void
711 bad_con()
712 {
713 	leave(1, "The game is full.  Sorry.");
714 	/* NOTREACHED */
715 }
716 
717 /*
718  * bad_ver:
719  *	version number mismatch.
720  */
721 void
722 bad_ver()
723 {
724 	leave(1, "Version number mismatch. No go.");
725 	/* NOTREACHED */
726 }
727 
728 /*
729  * sigterm:
730  *	Handle a terminate signal
731  */
732 SIGNAL_TYPE
733 sigterm(dummy)
734 	int dummy;
735 {
736 	leave(0, (char *) NULL);
737 	/* NOTREACHED */
738 }
739 
740 
741 /*
742  * sigemt:
743  *	Handle a emt signal - shouldn't happen on vaxes(?)
744  */
745 SIGNAL_TYPE
746 sigemt(dummy)
747 	int dummy;
748 {
749 	leave(1, "Unable to start driver.  Try again.");
750 	/* NOTREACHED */
751 }
752 
753 # ifdef INTERNET
754 /*
755  * sigalrm:
756  *	Handle an alarm signal
757  */
758 SIGNAL_TYPE
759 sigalrm(dummy)
760 	int dummy;
761 {
762 	return;
763 }
764 # endif
765 
766 /*
767  * rmnl:
768  *	Remove a '\n' at the end of a string if there is one
769  */
770 void
771 rmnl(s)
772 	char	*s;
773 {
774 	char	*cp;
775 
776 	cp = strrchr(s, '\n');
777 	if (cp != NULL)
778 		*cp = '\0';
779 }
780 
781 /*
782  * intr:
783  *	Handle a interrupt signal
784  */
785 SIGNAL_TYPE
786 intr(dummy)
787 	int dummy;
788 {
789 	int	ch;
790 	int	explained;
791 	int	y, x;
792 
793 	(void) signal(SIGINT, SIG_IGN);
794 # ifdef USE_CURSES
795 	getyx(stdscr, y, x);
796 	move(HEIGHT, 0);
797 # else
798 	y = cur_row;
799 	x = cur_col;
800 	mvcur(cur_row, cur_col, HEIGHT, 0);
801 	cur_row = HEIGHT;
802 	cur_col = 0;
803 # endif
804 	put_str("Really quit? ");
805 	clear_eol();
806 	refresh();
807 	explained = FALSE;
808 	for (;;) {
809 		ch = getchar();
810 		if (isupper(ch))
811 			ch = tolower(ch);
812 		if (ch == 'y') {
813 			if (Socket != 0) {
814 				(void) write(Socket, "q", 1);
815 				(void) close(Socket);
816 			}
817 			leave(0, (char *) NULL);
818 		}
819 		else if (ch == 'n') {
820 			(void) signal(SIGINT, intr);
821 # ifdef USE_CURSES
822 			move(y, x);
823 # else
824 			mvcur(cur_row, cur_col, y, x);
825 			cur_row = y;
826 			cur_col = x;
827 # endif
828 			refresh();
829 			return;
830 		}
831 		if (!explained) {
832 			put_str("(Yes or No) ");
833 			refresh();
834 			explained = TRUE;
835 		}
836 		beep();
837 		refresh();
838 	}
839 }
840 
841 /*
842  * leave:
843  *	Leave the game somewhat gracefully, restoring all current
844  *	tty stats.
845  */
846 void
847 leave(eval, mesg)
848 	int	eval;
849 	char	*mesg;
850 {
851 	if (in_visual) {
852 # ifdef USE_CURSES
853 		move(HEIGHT, 0);
854 		refresh();
855 		endwin();
856 # else /* !USE_CURSES */
857 		mvcur(cur_row, cur_col, HEIGHT, 0);
858 		(void) fflush(stdout);	/* flush in case VE changes pages */
859 # if defined(BSD_RELEASE) && BSD_RELEASE >= 44
860 		tcsetattr(0, TCSADRAIN, &__orig_termios);
861 # else
862 		resetty();
863 # endif
864 		_puts(VE);
865 		_puts(TE);
866 # endif /* !USE_CURSES */
867 	}
868 	if (mesg != NULL)
869 		puts(mesg);
870 	exit(eval);
871 }
872 
873 #if !defined(USE_CURSES) && defined(SIGTSTP)
874 /*
875  * tstp:
876  *	Handle stop and start signals
877  */
878 SIGNAL_TYPE
879 tstp(dummy)
880 	int dummy;
881 {
882 # if BSD_RELEASE < 44
883 	static struct sgttyb	tty;
884 # endif
885 	int	y, x;
886 
887 	y = cur_row;
888 	x = cur_col;
889 	mvcur(cur_row, cur_col, HEIGHT, 0);
890 	cur_row = HEIGHT;
891 	cur_col = 0;
892 # if !defined(BSD_RELEASE) || BSD_RELEASE < 44
893 	tty = _tty;
894 # endif
895 	_puts(VE);
896 	_puts(TE);
897 	(void) fflush(stdout);
898 # if defined(BSD_RELEASE) && BSD_RELEASE >= 44
899 	tcsetattr(0, TCSADRAIN, &__orig_termios);
900 # else
901 	resetty();
902 # endif
903 	(void) kill(getpid(), SIGSTOP);
904 	(void) signal(SIGTSTP, tstp);
905 # if defined(BSD_RELEASE) && BSD_RELEASE >= 44
906 	tcsetattr(0, TCSADRAIN, &saved_tty);
907 # else
908 	_tty = tty;
909 	ioctl(_tty_ch, TIOCSETP, &_tty);
910 # endif
911 	_puts(TI);
912 	_puts(VS);
913 	cur_row = y;
914 	cur_col = x;
915 	_puts(tgoto(CM, cur_row, cur_col));
916 	redraw_screen();
917 	(void) fflush(stdout);
918 }
919 #endif /* !defined(USE_CURSES) && defined(SIGTSTP) */
920 
921 # if defined(BSD_RELEASE) && BSD_RELEASE < 43
922 char *
923 strpbrk(s, brk)
924 	char *s, *brk;
925 {
926 	char *p;
927 	c;
928 
929 	while (c = *s) {
930 		for (p = brk; *p; p++)
931 			if (c == *p)
932 				return (s);
933 		s++;
934 	}
935 	return (0);
936 }
937 # endif
938 
939 long
940 env_init(enter_status)
941 	long	enter_status;
942 {
943 	int	i;
944 	char	*envp, *envname, *s;
945 
946 	for (i = 0; i < 256; i++)
947 		map_key[i] = (char) i;
948 
949 	envname = NULL;
950 	if ((envp = getenv("HUNT")) != NULL) {
951 		while ((s = strpbrk(envp, "=,")) != NULL) {
952 			if (strncmp(envp, "cloak,", s - envp + 1) == 0) {
953 				enter_status = Q_CLOAK;
954 				envp = s + 1;
955 			}
956 			else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
957 				enter_status = Q_SCAN;
958 				envp = s + 1;
959 			}
960 			else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
961 				enter_status = Q_FLY;
962 				envp = s + 1;
963 			}
964 			else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
965 				no_beep = TRUE;
966 				envp = s + 1;
967 			}
968 			else if (strncmp(envp, "name=", s - envp + 1) == 0) {
969 				envname = s + 1;
970 				if ((s = strchr(envp, ',')) == NULL) {
971 					*envp = '\0';
972 					strncpy(name, envname, NAMELEN);
973 					break;
974 				}
975 				*s = '\0';
976 				strncpy(name, envname, NAMELEN);
977 				envp = s + 1;
978 			}
979 # ifdef INTERNET
980 			else if (strncmp(envp, "port=", s - envp + 1) == 0) {
981 				use_port = s + 1;
982 				Test_port = atoi(use_port);
983 				if ((s = strchr(envp, ',')) == NULL) {
984 					*envp = '\0';
985 					break;
986 				}
987 				*s = '\0';
988 				envp = s + 1;
989 			}
990 			else if (strncmp(envp, "host=", s - envp + 1) == 0) {
991 				Sock_host = s + 1;
992 				if ((s = strchr(envp, ',')) == NULL) {
993 					*envp = '\0';
994 					break;
995 				}
996 				*s = '\0';
997 				envp = s + 1;
998 			}
999 			else if (strncmp(envp, "message=", s - envp + 1) == 0) {
1000 				Send_message = s + 1;
1001 				if ((s = strchr(envp, ',')) == NULL) {
1002 					*envp = '\0';
1003 					break;
1004 				}
1005 				*s = '\0';
1006 				envp = s + 1;
1007 			}
1008 # endif
1009 			else if (strncmp(envp, "team=", s - envp + 1) == 0) {
1010 				team = *(s + 1);
1011 				if (!isdigit(team))
1012 					team = ' ';
1013 				if ((s = strchr(envp, ',')) == NULL) {
1014 					*envp = '\0';
1015 					break;
1016 				}
1017 				*s = '\0';
1018 				envp = s + 1;
1019 			}			/* must be last option */
1020 			else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) {
1021 				for (s = s + 1; *s != '\0'; s += 2) {
1022 					map_key[(unsigned int) *s] = *(s + 1);
1023 					if (*(s + 1) == '\0') {
1024 						break;
1025 					}
1026 				}
1027 				*envp = '\0';
1028 				break;
1029 			} else {
1030 				*s = '\0';
1031 				printf("unknown option %s\n", envp);
1032 				if ((s = strchr(envp, ',')) == NULL) {
1033 					*envp = '\0';
1034 					break;
1035 				}
1036 				envp = s + 1;
1037 			}
1038 		}
1039 		if (*envp != '\0') {
1040 			if (envname == NULL)
1041 				strncpy(name, envp, NAMELEN);
1042 			else
1043 				printf("unknown option %s\n", envp);
1044 		}
1045 	}
1046 	return enter_status;
1047 }
1048 
1049 void
1050 fill_in_blanks()
1051 {
1052 	int	i;
1053 	char	*cp;
1054 
1055 again:
1056 	if (name[0] != '\0') {
1057 		printf("Entering as '%s'", name);
1058 		if (team != ' ')
1059 			printf(" on team %c.\n", team);
1060 		else
1061 			putchar('\n');
1062 	} else {
1063 		printf("Enter your code name: ");
1064 		if (fgets(name, NAMELEN, stdin) == NULL)
1065 			exit(1);
1066 	}
1067 	rmnl(name);
1068 	if (name[0] == '\0') {
1069 		name[0] = '\0';
1070 		printf("You have to have a code name!\n");
1071 		goto again;
1072 	}
1073 	for (cp = name; *cp != '\0'; cp++)
1074 		if (!isprint(*cp)) {
1075 			name[0] = '\0';
1076 			printf("Illegal character in your code name.\n");
1077 			goto again;
1078 		}
1079 	if (team == ' ') {
1080 		printf("Enter your team (0-9 or nothing): ");
1081 		i = getchar();
1082 		if (isdigit(i))
1083 			team = i;
1084 		while (i != '\n' && i != EOF)
1085 			i = getchar();
1086 	}
1087 }
1088