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