1/*	$NetBSD: hunt.c,v 1.40 2011/08/31 16:24:56 plunky Exp $	*/
2/*
3 * Copyright (c) 1983-2003, Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * + Redistributions of source code must retain the above copyright
11 *   notice, this list of conditions and the following disclaimer.
12 * + Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 * + Neither the name of the University of California, San Francisco nor
16 *   the names of its contributors may be used to endorse or promote
17 *   products derived from this software without specific prior written
18 *   permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34#ifndef lint
35__RCSID("$NetBSD: hunt.c,v 1.40 2011/08/31 16:24:56 plunky Exp $");
36#endif /* not lint */
37
38#include <sys/param.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41#include <sys/poll.h>
42#include <ctype.h>
43#include <err.h>
44#include <errno.h>
45#include <curses.h>
46#include <signal.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50#include <ifaddrs.h>
51
52#include "hunt.h"
53
54#define clear_eol()	clrtoeol()
55#define put_ch		addch
56#define put_str		addstr
57
58FLAG Last_player = FALSE;
59#ifdef MONITOR
60FLAG Am_monitor = FALSE;
61#endif
62
63char Buf[BUFSIZ];
64
65/*static*/ int Socket;
66#ifdef INTERNET
67static char *Sock_host;
68static char *use_port;
69static FLAG Query_driver = FALSE;
70char *Send_message = NULL;
71static FLAG Show_scores = FALSE;
72#endif
73
74static SOCKET Daemon;
75#ifdef INTERNET
76#define DAEMON_SIZE	(sizeof Daemon)
77#else
78#define DAEMON_SIZE	(sizeof Daemon - 1)
79#endif
80
81char map_key[256];			/* what to map keys to */
82FLAG no_beep;
83
84static char name[NAMELEN];
85static char team = ' ';
86
87static int in_visual;
88
89extern int cur_row, cur_col;
90
91static void dump_scores(SOCKET);
92static long env_init(long);
93static void fill_in_blanks(void);
94static void leave(int, const char *) __dead;
95static void leavex(int, const char *) __dead;
96static void fincurs(void);
97static void rmnl(char *);
98static void sigterm(int) __dead;
99static void sigusr1(int) __dead;
100static void find_driver(FLAG);
101static void start_driver(void);
102static int broadcast_vec(int, struct sockaddr **);
103#ifdef INTERNET
104static SOCKET *list_drivers(void);
105#endif
106
107extern int Otto_mode;
108/*
109 * main:
110 *	Main program for local process
111 */
112int
113main(int ac, char **av)
114{
115	char *term;
116	int c;
117	long enter_status;
118
119	enter_status = env_init((long) Q_CLOAK);
120	while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != -1) {
121		switch (c) {
122		case 'l':	/* rsh compatibility */
123		case 'n':
124			(void) strncpy(name, optarg, NAMELEN);
125			break;
126		case 't':
127			team = *optarg;
128			if (!isdigit((unsigned char)team)) {
129				warnx("Team names must be numeric");
130				team = ' ';
131			}
132			break;
133		case 'o':
134#ifndef OTTO
135			warnx("The -o flag is reserved for future use.");
136			goto usage;
137#else
138			Otto_mode = TRUE;
139			break;
140#endif
141		case 'm':
142#ifdef MONITOR
143			Am_monitor = TRUE;
144#else
145			warnx("The monitor was not compiled in.");
146#endif
147			break;
148#ifdef INTERNET
149		case 'S':
150			Show_scores = TRUE;
151			break;
152		case 'q':	/* query whether hunt is running */
153			Query_driver = TRUE;
154			break;
155		case 'w':
156			Send_message = optarg;
157			break;
158		case 'h':
159			Sock_host = optarg;
160			break;
161		case 'p':
162			use_port = optarg;
163			Test_port = atoi(use_port);
164			break;
165#else
166		case 'S':
167		case 'q':
168		case 'w':
169		case 'h':
170		case 'p':
171			wanrx("Need TCP/IP for S, q, w, h, and p options.");
172			break;
173#endif
174		case 'c':
175			enter_status = Q_CLOAK;
176			break;
177		case 'f':
178#ifdef FLY
179			enter_status = Q_FLY;
180#else
181			warnx("The flying code was not compiled in.");
182#endif
183			break;
184		case 's':
185			enter_status = Q_SCAN;
186			break;
187		case 'b':
188			no_beep = !no_beep;
189			break;
190		default:
191		usage:
192			fputs(
193"usage:\thunt [-qmcsfS] [-n name] [-t team] [-p port] [-w message] [host]\n",
194			stderr);
195			exit(1);
196		}
197	}
198#ifdef INTERNET
199	if (optind + 1 < ac)
200		goto usage;
201	else if (optind + 1 == ac)
202		Sock_host = av[ac - 1];
203#else
204	if (optind > ac)
205		goto usage;
206#endif
207
208#ifdef INTERNET
209	if (Show_scores) {
210		SOCKET *hosts;
211
212		for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1)
213			dump_scores(*hosts);
214		exit(0);
215	}
216	if (Query_driver) {
217		SOCKET *hosts;
218
219		for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1) {
220			struct hostent *hp;
221			int num_players;
222
223			hp = gethostbyaddr((char *) &hosts->sin_addr,
224					sizeof hosts->sin_addr, AF_INET);
225			num_players = ntohs(hosts->sin_port);
226			printf("%d player%s hunting on %s!\n",
227				num_players, (num_players == 1) ? "" : "s",
228				hp != NULL ? hp->h_name :
229				inet_ntoa(hosts->sin_addr));
230		}
231		exit(0);
232	}
233#endif
234#ifdef OTTO
235	if (Otto_mode)
236		(void) strncpy(name, "otto", NAMELEN);
237	else
238#endif
239	fill_in_blanks();
240
241	(void) fflush(stdout);
242	if (!isatty(0) || (term = getenv("TERM")) == NULL)
243		errx(1, "no terminal type");
244	if (!initscr())
245		errx(0, "couldn't initialize screen");
246	(void) noecho();
247	(void) cbreak();
248	in_visual = TRUE;
249	if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH)
250		leavex(1, "Need a larger window");
251	clear_the_screen();
252	(void) signal(SIGINT, intr);
253	(void) signal(SIGTERM, sigterm);
254	(void) signal(SIGUSR1, sigusr1);
255	(void) signal(SIGPIPE, SIG_IGN);
256
257	for (;;) {
258#ifdef INTERNET
259		find_driver(TRUE);
260
261		if (Daemon.sin_port == 0)
262			leavex(1, "Game not found, try again");
263
264	jump_in:
265		do {
266			int option;
267
268			Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
269			if (Socket < 0)
270				err(1, "socket");
271			option = 1;
272			if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
273			    &option, sizeof option) < 0)
274				warn("setsockopt loopback");
275			errno = 0;
276			if (connect(Socket, (struct sockaddr *) &Daemon,
277			    DAEMON_SIZE) < 0) {
278				if (errno != ECONNREFUSED) {
279					leave(1, "connect");
280				}
281			}
282			else
283				break;
284			sleep(1);
285		} while (close(Socket) == 0);
286#else /* !INTERNET */
287		/*
288		 * set up a socket
289		 */
290
291		if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0)
292			err(1, "socket");
293
294		/*
295		 * attempt to connect the socket to a name; if it fails that
296		 * usually means that the driver isn't running, so we start
297		 * up the driver.
298		 */
299
300		Daemon.sun_family = SOCK_FAMILY;
301		(void) strcpy(Daemon.sun_path, Sock_name);
302		if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) {
303			if (errno != ENOENT) {
304				leavex(1, "connect2");
305			}
306			start_driver();
307
308			do {
309				(void) close(Socket);
310				if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM,
311				    0)) < 0)
312					err(1, "socket");
313				sleep(2);
314			} while (connect(Socket, &Daemon, DAEMON_SIZE) < 0);
315		}
316#endif
317
318		do_connect(name, team, enter_status);
319#ifdef INTERNET
320		if (Send_message != NULL) {
321			do_message();
322			if (enter_status == Q_MESSAGE)
323				break;
324			Send_message = NULL;
325			/* don't continue as that will call find_driver */
326			goto jump_in;
327		}
328#endif
329		playit();
330		if ((enter_status = quit(enter_status)) == Q_QUIT)
331			break;
332	}
333	leavex(0, NULL);
334	/* NOTREACHED */
335	return(0);
336}
337
338#ifdef INTERNET
339static int
340broadcast_vec(int s /*socket*/, struct sockaddr **vector)
341{
342	int vec_cnt;
343	struct ifaddrs *ifp, *ip;
344
345	*vector = NULL;
346	if (getifaddrs(&ifp) < 0)
347		return 0;
348
349	vec_cnt = 0;
350	for (ip = ifp; ip; ip = ip->ifa_next)
351		if ((ip->ifa_addr->sa_family == AF_INET) &&
352		    (ip->ifa_flags & IFF_BROADCAST))
353			vec_cnt++;
354
355	*vector = (struct sockaddr *)
356		malloc(vec_cnt * sizeof(struct sockaddr_in));
357
358	vec_cnt = 0;
359	for (ip = ifp; ip; ip = ip->ifa_next)
360		if ((ip->ifa_addr->sa_family == AF_INET) &&
361		    (ip->ifa_flags & IFF_BROADCAST))
362			memcpy(&(*vector)[vec_cnt++], ip->ifa_broadaddr,
363			       sizeof(struct sockaddr_in));
364
365	freeifaddrs(ifp);
366	return vec_cnt;
367}
368
369SOCKET *
370list_drivers(void)
371{
372	int option;
373	u_short msg;
374	u_short port_num;
375	static SOCKET test;
376	int test_socket;
377	socklen_t namelen;
378	char local_name[MAXHOSTNAMELEN + 1];
379	static int initial = TRUE;
380	static struct in_addr local_address;
381	struct hostent *hp;
382	static int brdc;
383	static SOCKET *brdv;
384	int i;
385	unsigned j;
386	static SOCKET *listv;
387	static unsigned int listmax;
388	unsigned int listc;
389	struct pollfd set[1];
390
391	if (initial) {			/* do one time initialization */
392		if (gethostname(local_name, sizeof local_name) < 0) {
393			leavex(1, "Sorry, I have no name.");
394			/* NOTREACHED */
395		}
396		local_name[sizeof(local_name) - 1] = '\0';
397		if ((hp = gethostbyname(local_name)) == NULL) {
398			leavex(1, "Can't find myself.");
399			/* NOTREACHED */
400		}
401		local_address = * ((struct in_addr *) hp->h_addr);
402
403		listmax = 20;
404		listv = (SOCKET *) malloc(listmax * sizeof (SOCKET));
405	} else if (Sock_host != NULL)
406		return listv;		/* address already valid */
407
408	test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
409	if (test_socket < 0) {
410		leave(1, "socket system call failed");
411		/* NOTREACHED */
412	}
413	test.sin_family = SOCK_FAMILY;
414	test.sin_port = htons(Test_port);
415	listc = 0;
416
417	if (Sock_host != NULL) {	/* explicit host given */
418		if ((hp = gethostbyname(Sock_host)) == NULL) {
419			leavex(1, "Unknown host");
420			/* NOTREACHED */
421		}
422		test.sin_addr = *((struct in_addr *) hp->h_addr);
423		goto test_one_host;
424	}
425
426	if (!initial) {
427		/* favor host of previous session by broadcasting to it first */
428		test.sin_addr = Daemon.sin_addr;
429		msg = htons(C_PLAYER);		/* Must be playing! */
430		(void) sendto(test_socket, &msg, sizeof msg, 0,
431		    (struct sockaddr *) &test, DAEMON_SIZE);
432	}
433
434	if (initial)
435		brdc = broadcast_vec(test_socket, (void *) &brdv);
436
437#ifdef SO_BROADCAST
438	/* Sun's will broadcast even though this option can't be set */
439	option = 1;
440	if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST,
441	    &option, sizeof option) < 0) {
442		leave(1, "setsockopt broadcast");
443		/* NOTREACHED */
444	}
445#endif
446
447	/* send broadcast packets on all interfaces */
448	msg = htons(C_TESTMSG());
449	for (i = 0; i < brdc; i++) {
450		test.sin_addr = brdv[i].sin_addr;
451		if (sendto(test_socket, &msg, sizeof msg, 0,
452		    (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
453			leave(1, "sendto");
454			/* NOTREACHED */
455		}
456	}
457	test.sin_addr = local_address;
458	if (sendto(test_socket, &msg, sizeof msg, 0,
459	    (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
460		leave(1, "sendto");
461		/* NOTREACHED */
462	}
463
464get_response:
465	namelen = DAEMON_SIZE;
466	errno = 0;
467	set[0].fd = test_socket;
468	set[0].events = POLLIN;
469	for (;;) {
470		if (listc + 1 >= listmax) {
471			SOCKET *newlistv;
472
473			listmax += 20;
474			newlistv = realloc(listv, listmax * sizeof(*listv));
475			if (newlistv == NULL)
476				leave(1, "realloc");
477			listv = newlistv;
478		}
479
480		if (poll(set, 1, 1000) == 1 &&
481		    recvfrom(test_socket, &port_num, sizeof(port_num),
482		    0, (struct sockaddr *) &listv[listc], &namelen) > 0) {
483			/*
484			 * Note that we do *not* convert from network to host
485			 * order since the port number *should* be in network
486			 * order:
487			 */
488			for (j = 0; j < listc; j += 1)
489				if (listv[listc].sin_addr.s_addr
490				== listv[j].sin_addr.s_addr)
491					break;
492			if (j == listc)
493				listv[listc++].sin_port = port_num;
494			continue;
495		}
496
497		if (errno != 0 && errno != EINTR) {
498			leave(1, "poll/recvfrom");
499			/* NOTREACHED */
500		}
501
502		/* terminate list with local address */
503		listv[listc].sin_family = SOCK_FAMILY;
504		listv[listc].sin_addr = local_address;
505		listv[listc].sin_port = htons(0);
506
507		(void) close(test_socket);
508		initial = FALSE;
509		return listv;
510	}
511
512test_one_host:
513	msg = htons(C_TESTMSG());
514	(void) sendto(test_socket, &msg, sizeof msg, 0,
515	    (struct sockaddr *) &test, DAEMON_SIZE);
516	goto get_response;
517}
518
519static void
520find_driver(FLAG do_startup)
521{
522	SOCKET *hosts;
523
524	hosts = list_drivers();
525	if (hosts[0].sin_port != htons(0)) {
526		int i, c;
527
528		if (hosts[1].sin_port == htons(0)) {
529			Daemon = hosts[0];
530			return;
531		}
532		/* go thru list and return host that matches daemon */
533		clear_the_screen();
534		move(1, 0);
535		put_str("Pick one:");
536		for (i = 0; i < HEIGHT - 4 && hosts[i].sin_port != htons(0);
537								i += 1) {
538			struct hostent *hp;
539			char buf[80];
540
541			move(3 + i, 0);
542			hp = gethostbyaddr((char *) &hosts[i].sin_addr,
543					sizeof hosts[i].sin_addr, AF_INET);
544			(void) snprintf(buf, sizeof(buf),
545				"%8c    %.64s", 'a' + i,
546				hp != NULL ? hp->h_name
547				: inet_ntoa(hosts->sin_addr));
548			put_str(buf);
549		}
550		move(4 + i, 0);
551		put_str("Enter letter: ");
552		refresh();
553		while (!islower(c = getchar()) || (c -= 'a') >= i) {
554			beep();
555			refresh();
556		}
557		Daemon = hosts[c];
558		clear_the_screen();
559		return;
560	}
561	if (!do_startup)
562		return;
563
564	start_driver();
565	sleep(2);
566	find_driver(FALSE);
567}
568
569static void
570dump_scores(SOCKET host)
571{
572	struct hostent *hp;
573	int s;
574	char buf[BUFSIZ];
575	int cnt;
576
577	hp = gethostbyaddr((char *) &host.sin_addr, sizeof host.sin_addr,
578								AF_INET);
579	printf("\n%s:\n", hp != NULL ? hp->h_name : inet_ntoa(host.sin_addr));
580	fflush(stdout);
581
582	s = socket(SOCK_FAMILY, SOCK_STREAM, 0);
583	if (s < 0)
584		err(1, "socket");
585	if (connect(s, (struct sockaddr *) &host, sizeof host) < 0)
586		err(1, "connect");
587	while ((cnt = read(s, buf, BUFSIZ)) > 0)
588		write(fileno(stdout), buf, cnt);
589	(void) close(s);
590}
591
592#endif
593
594static void
595start_driver(void)
596{
597	int procid;
598
599#ifdef MONITOR
600	if (Am_monitor) {
601		leavex(1, "No one playing.");
602		/* NOTREACHED */
603	}
604#endif
605
606#ifdef INTERNET
607	if (Sock_host != NULL) {
608		sleep(3);
609		return;
610	}
611#endif
612
613	move(HEIGHT, 0);
614	put_str("Starting...");
615	refresh();
616	procid = fork();
617	if (procid == -1) {
618		leave(1, "fork failed.");
619	}
620	if (procid == 0) {
621		(void) signal(SIGINT, SIG_IGN);
622#ifndef INTERNET
623		(void) close(Socket);
624#else
625		if (use_port == NULL)
626#endif
627			execl(Driver, "HUNT", (char *) NULL);
628#ifdef INTERNET
629		else
630			execl(Driver, "HUNT", "-p", use_port, (char *) NULL);
631#endif
632		/* only get here if exec failed */
633		(void) kill(getppid(), SIGUSR1);	/* tell mom */
634		_exit(1);
635	}
636	move(HEIGHT, 0);
637	put_str("Connecting...");
638	refresh();
639}
640
641/*
642 * bad_con:
643 *	We had a bad connection.  For the moment we assume that this
644 *	means the game is full.
645 */
646void
647bad_con(void)
648{
649	leavex(1, "The game is full.  Sorry.");
650	/* NOTREACHED */
651}
652
653/*
654 * bad_ver:
655 *	version number mismatch.
656 */
657void
658bad_ver(void)
659{
660	leavex(1, "Version number mismatch. No go.");
661	/* NOTREACHED */
662}
663
664/*
665 * sigterm:
666 *	Handle a terminate signal
667 */
668static void
669sigterm(int dummy __unused)
670{
671	leavex(0, NULL);
672	/* NOTREACHED */
673}
674
675
676/*
677 * sigusr1:
678 *	Handle a usr1 signal
679 */
680static void
681sigusr1(int dummy __unused)
682{
683	leavex(1, "Unable to start driver.  Try again.");
684	/* NOTREACHED */
685}
686
687/*
688 * rmnl:
689 *	Remove a '\n' at the end of a string if there is one
690 */
691static void
692rmnl(char *s)
693{
694	char *cp;
695
696	cp = strrchr(s, '\n');
697	if (cp != NULL)
698		*cp = '\0';
699}
700
701/*
702 * intr:
703 *	Handle a interrupt signal
704 */
705void
706intr(int dummy __unused)
707{
708	int ch;
709	int explained;
710	int y, x;
711
712	(void) signal(SIGINT, SIG_IGN);
713	getyx(stdscr, y, x);
714	move(HEIGHT, 0);
715	put_str("Really quit? ");
716	clear_eol();
717	refresh();
718	explained = FALSE;
719	for (;;) {
720		ch = getchar();
721		if (isupper(ch))
722			ch = tolower(ch);
723		if (ch == 'y') {
724			if (Socket != 0) {
725				(void) write(Socket, "q", 1);
726				(void) close(Socket);
727			}
728			leavex(0, NULL);
729		}
730		else if (ch == 'n') {
731			(void) signal(SIGINT, intr);
732			move(y, x);
733			refresh();
734			return;
735		}
736		if (!explained) {
737			put_str("(Yes or No) ");
738			refresh();
739			explained = TRUE;
740		}
741		beep();
742		refresh();
743	}
744}
745
746static void
747fincurs(void)
748{
749	if (in_visual) {
750		move(HEIGHT, 0);
751		refresh();
752		endwin();
753	}
754}
755
756/*
757 * leave:
758 *	Leave the game somewhat gracefully, restoring all current
759 *	tty stats, and print errno.
760 */
761void
762leave(int eval, const char *mesg)
763{
764	int serrno = errno;
765	fincurs();
766	errno = serrno;
767	err(eval, "%s", mesg ? mesg : "");
768}
769
770/*
771 * leavex:
772 *	Leave the game somewhat gracefully, restoring all current
773 *	tty stats.
774 */
775void
776leavex(int eval, const char *mesg)
777{
778	fincurs();
779	errx(eval, "%s", mesg ? mesg : "");
780}
781
782static long
783env_init(long enter_status)
784{
785	int i;
786	char *envp, *envname, *s;
787
788	for (i = 0; i < 256; i++)
789		map_key[i] = (char) i;
790
791	envname = NULL;
792	if ((envp = getenv("HUNT")) != NULL) {
793		while ((s = strpbrk(envp, "=,")) != NULL) {
794			if (strncmp(envp, "cloak,", s - envp + 1) == 0) {
795				enter_status = Q_CLOAK;
796				envp = s + 1;
797			}
798			else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
799				enter_status = Q_SCAN;
800				envp = s + 1;
801			}
802			else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
803				enter_status = Q_FLY;
804				envp = s + 1;
805			}
806			else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
807				no_beep = TRUE;
808				envp = s + 1;
809			}
810			else if (strncmp(envp, "name=", s - envp + 1) == 0) {
811				envname = s + 1;
812				if ((s = strchr(envp, ',')) == NULL) {
813					*envp = '\0';
814					strncpy(name, envname, NAMELEN);
815					break;
816				}
817				*s = '\0';
818				strncpy(name, envname, NAMELEN);
819				envp = s + 1;
820			}
821#ifdef INTERNET
822			else if (strncmp(envp, "port=", s - envp + 1) == 0) {
823				use_port = s + 1;
824				Test_port = atoi(use_port);
825				if ((s = strchr(envp, ',')) == NULL) {
826					*envp = '\0';
827					break;
828				}
829				*s = '\0';
830				envp = s + 1;
831			}
832			else if (strncmp(envp, "host=", s - envp + 1) == 0) {
833				Sock_host = s + 1;
834				if ((s = strchr(envp, ',')) == NULL) {
835					*envp = '\0';
836					break;
837				}
838				*s = '\0';
839				envp = s + 1;
840			}
841			else if (strncmp(envp, "message=", s - envp + 1) == 0) {
842				Send_message = s + 1;
843				if ((s = strchr(envp, ',')) == NULL) {
844					*envp = '\0';
845					break;
846				}
847				*s = '\0';
848				envp = s + 1;
849			}
850#endif
851			else if (strncmp(envp, "team=", s - envp + 1) == 0) {
852				team = *(s + 1);
853				if (!isdigit((unsigned char)team))
854					team = ' ';
855				if ((s = strchr(envp, ',')) == NULL) {
856					*envp = '\0';
857					break;
858				}
859				*s = '\0';
860				envp = s + 1;
861			}			/* must be last option */
862			else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) {
863				for (s = s + 1; *s != '\0'; s += 2) {
864					map_key[(unsigned int) *s] = *(s + 1);
865					if (*(s + 1) == '\0') {
866						break;
867					}
868				}
869				*envp = '\0';
870				break;
871			} else {
872				*s = '\0';
873				printf("unknown option %s\n", envp);
874				if ((s = strchr(envp, ',')) == NULL) {
875					*envp = '\0';
876					break;
877				}
878				envp = s + 1;
879			}
880		}
881		if (*envp != '\0') {
882			if (envname == NULL)
883				strncpy(name, envp, NAMELEN);
884			else
885				printf("unknown option %s\n", envp);
886		}
887	}
888	return enter_status;
889}
890
891static void
892fill_in_blanks(void)
893{
894	int i;
895	char *cp;
896
897again:
898	if (name[0] != '\0') {
899		printf("Entering as '%s'", name);
900		if (team != ' ')
901			printf(" on team %c.\n", team);
902		else
903			putchar('\n');
904	} else {
905		printf("Enter your code name: ");
906		if (fgets(name, NAMELEN, stdin) == NULL)
907			exit(1);
908	}
909	rmnl(name);
910	if (name[0] == '\0') {
911		name[0] = '\0';
912		printf("You have to have a code name!\n");
913		goto again;
914	}
915	for (cp = name; *cp != '\0'; cp++)
916		if (!isprint((unsigned char)*cp)) {
917			name[0] = '\0';
918			printf("Illegal character in your code name.\n");
919			goto again;
920		}
921	if (team == ' ') {
922		printf("Enter your team (0-9 or nothing): ");
923		i = getchar();
924		if (isdigit(i))
925			team = i;
926		while (i != '\n' && i != EOF)
927			i = getchar();
928	}
929}
930