Home | History | Annotate | Line # | Download | only in hunt
      1 /*	$NetBSD: hunt.c,v 1.58 2014/03/30 09:11:50 skrll 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.58 2014/03/30 09:11:50 skrll 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 <assert.h>
     51 
     52 #include "hunt_common.h"
     53 #include "pathnames.h"
     54 #include "hunt_private.h"
     55 
     56 
     57 #ifdef OVERRIDE_PATH_HUNTD
     58 static const char Driver[] = OVERRIDE_PATH_HUNTD;
     59 #else
     60 static const char Driver[] = PATH_HUNTD;
     61 #endif
     62 
     63 #ifdef INTERNET
     64 static const char *contactportstr;
     65 static uint16_t contactport = TEST_PORT;
     66 static const char *contacthost;
     67 #else
     68 static const char huntsockpath[] = PATH_HUNTSOCKET;
     69 #endif
     70 
     71 
     72 bool Last_player = false;
     73 #ifdef MONITOR
     74 bool Am_monitor = false;
     75 #endif
     76 
     77 char Buf[BUFSIZ];
     78 
     79 /*static*/ int huntsocket;
     80 #ifdef INTERNET
     81 char *Send_message = NULL;
     82 #endif
     83 
     84 SOCKET Daemon;
     85 #ifdef INTERNET
     86 #define DAEMON_SIZE	(sizeof Daemon)
     87 #else
     88 #define DAEMON_SIZE	(sizeof Daemon - 1)
     89 #endif
     90 
     91 char map_key[256];			/* what to map keys to */
     92 bool no_beep;
     93 
     94 static char name[WIRE_NAMELEN];
     95 static char team = ' ';
     96 
     97 static int in_visual;
     98 
     99 extern int cur_row, cur_col;
    100 
    101 static void dump_scores(const struct sockaddr_storage *, socklen_t);
    102 static int env_init(int);
    103 static void fill_in_blanks(void);
    104 static void fincurs(void);
    105 static void rmnl(char *);
    106 static void sigterm(int) __dead;
    107 static void sigusr1(int) __dead;
    108 static void find_driver(void);
    109 static void start_driver(void);
    110 
    111 extern int Otto_mode;
    112 
    113 static const char *
    114 lookuphost(const struct sockaddr_storage *host, socklen_t hostlen)
    115 {
    116 	static char buf[NI_MAXHOST];
    117 	int result;
    118 
    119 	result = getnameinfo((const struct sockaddr *)host, hostlen,
    120 			     buf, sizeof(buf), NULL, 0, NI_NOFQDN);
    121 	if (result) {
    122 		leavex(1, "getnameinfo: %s", gai_strerror(result));
    123 	}
    124 	return buf;
    125 }
    126 
    127 /*
    128  * main:
    129  *	Main program for local process
    130  */
    131 int
    132 main(int ac, char **av)
    133 {
    134 	char *term;
    135 	int c;
    136 	int enter_status;
    137 	bool Query_driver = false;
    138 	bool Show_scores = false;
    139 
    140 	enter_status = env_init(Q_CLOAK);
    141 	while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != -1) {
    142 		switch (c) {
    143 		case 'l':	/* rsh compatibility */
    144 		case 'n':
    145 			(void) strncpy(name, optarg, sizeof(name));
    146 			break;
    147 		case 't':
    148 			team = *optarg;
    149 			if (!isdigit((unsigned char)team)) {
    150 				warnx("Team names must be numeric");
    151 				team = ' ';
    152 			}
    153 			break;
    154 		case 'o':
    155 #ifndef OTTO
    156 			warnx("The -o flag is reserved for future use.");
    157 			goto usage;
    158 #else
    159 			Otto_mode = true;
    160 			break;
    161 #endif
    162 		case 'm':
    163 #ifdef MONITOR
    164 			Am_monitor = true;
    165 #else
    166 			warnx("The monitor was not compiled in.");
    167 #endif
    168 			break;
    169 #ifdef INTERNET
    170 		case 'S':
    171 			Show_scores = true;
    172 			break;
    173 		case 'q':	/* query whether hunt is running */
    174 			Query_driver = true;
    175 			break;
    176 		case 'w':
    177 			Send_message = optarg;
    178 			break;
    179 		case 'h':
    180 			contacthost = optarg;
    181 			break;
    182 		case 'p':
    183 			contactportstr = optarg;
    184 			contactport = atoi(contactportstr);
    185 			break;
    186 #else
    187 		case 'S':
    188 		case 'q':
    189 		case 'w':
    190 		case 'h':
    191 		case 'p':
    192 			wanrx("Need TCP/IP for S, q, w, h, and p options.");
    193 			break;
    194 #endif
    195 		case 'c':
    196 			enter_status = Q_CLOAK;
    197 			break;
    198 		case 'f':
    199 #ifdef FLY
    200 			enter_status = Q_FLY;
    201 #else
    202 			warnx("The flying code was not compiled in.");
    203 #endif
    204 			break;
    205 		case 's':
    206 			enter_status = Q_SCAN;
    207 			break;
    208 		case 'b':
    209 			no_beep = !no_beep;
    210 			break;
    211 		default:
    212 		usage:
    213 			fputs(
    214 "usage:\thunt [-qmcsfS] [-n name] [-t team] [-p port] [-w message] [host]\n",
    215 			stderr);
    216 			exit(1);
    217 		}
    218 	}
    219 #ifdef INTERNET
    220 	if (optind + 1 < ac)
    221 		goto usage;
    222 	else if (optind + 1 == ac)
    223 		contacthost = av[ac - 1];
    224 #else
    225 	if (optind < ac)
    226 		goto usage;
    227 #endif
    228 
    229 #ifdef INTERNET
    230 	serverlist_setup(contacthost, contactport);
    231 
    232 	if (Show_scores) {
    233 		const struct sockaddr_storage *host;
    234 		socklen_t hostlen;
    235 		u_short msg = C_SCORES;
    236 		unsigned i;
    237 
    238 		serverlist_query(msg);
    239 		for (i = 0; i < serverlist_num(); i++) {
    240 			host = serverlist_gethost(i, &hostlen);
    241 			dump_scores(host, hostlen);
    242 		}
    243 		exit(0);
    244 	}
    245 	if (Query_driver) {
    246 		const struct sockaddr_storage *host;
    247 		socklen_t hostlen;
    248 		u_short msg = C_MESSAGE;
    249 		u_short num_players;
    250 		unsigned i;
    251 
    252 		serverlist_query(msg);
    253 		for (i = 0; i < serverlist_num(); i++) {
    254 			host = serverlist_gethost(i, &hostlen);
    255 			num_players = ntohs(serverlist_getresponse(i));
    256 
    257 			printf("%d player%s hunting on %s!\n",
    258 				num_players, (num_players == 1) ? "" : "s",
    259 				lookuphost(host, hostlen));
    260 		}
    261 		exit(0);
    262 	}
    263 #endif
    264 #ifdef OTTO
    265 	if (Otto_mode)
    266 		(void) strncpy(name, "otto", sizeof(name));
    267 	else
    268 #endif
    269 	fill_in_blanks();
    270 
    271 	(void) fflush(stdout);
    272 	if (!isatty(0) || (term = getenv("TERM")) == NULL)
    273 		errx(1, "no terminal type");
    274 	if (!initscr())
    275 		errx(0, "couldn't initialize screen");
    276 	(void) noecho();
    277 	(void) cbreak();
    278 	in_visual = true;
    279 	if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH)
    280 		leavex(1, "Need a larger window");
    281 	clear_the_screen();
    282 	(void) signal(SIGINT, intr);
    283 	(void) signal(SIGTERM, sigterm);
    284 	(void) signal(SIGUSR1, sigusr1);
    285 	(void) signal(SIGPIPE, SIG_IGN);
    286 
    287 	for (;;) {
    288 #ifdef INTERNET
    289 		find_driver();
    290 
    291 		if (Daemon.sin_port == 0)
    292 			leavex(1, "Game not found, try again");
    293 
    294 	jump_in:
    295 		do {
    296 			int option;
    297 
    298 			huntsocket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
    299 			if (huntsocket < 0)
    300 				err(1, "socket");
    301 			option = 1;
    302 			if (setsockopt(huntsocket, SOL_SOCKET, SO_USELOOPBACK,
    303 			    &option, sizeof option) < 0)
    304 				warn("setsockopt loopback");
    305 			errno = 0;
    306 			if (connect(huntsocket, (struct sockaddr *) &Daemon,
    307 			    DAEMON_SIZE) < 0) {
    308 				if (errno != ECONNREFUSED) {
    309 					leave(1, "connect");
    310 				}
    311 			}
    312 			else
    313 				break;
    314 			sleep(1);
    315 		} while (close(huntsocket) == 0);
    316 #else /* !INTERNET */
    317 		/*
    318 		 * set up a socket
    319 		 */
    320 
    321 		if ((huntsocket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0)
    322 			err(1, "socket");
    323 
    324 		/*
    325 		 * attempt to connect the socket to a name; if it fails that
    326 		 * usually means that the driver isn't running, so we start
    327 		 * up the driver.
    328 		 */
    329 
    330 		Daemon.sun_family = SOCK_FAMILY;
    331 		(void) strcpy(Daemon.sun_path, huntsockpath);
    332 		if (connect(huntsocket, &Daemon, DAEMON_SIZE) < 0) {
    333 			if (errno != ENOENT) {
    334 				leavex(1, "connect2");
    335 			}
    336 			start_driver();
    337 
    338 			do {
    339 				(void) close(huntsocket);
    340 				if ((huntsocket = socket(SOCK_FAMILY, SOCK_STREAM,
    341 				    0)) < 0)
    342 					err(1, "socket");
    343 				sleep(2);
    344 			} while (connect(huntsocket, &Daemon, DAEMON_SIZE) < 0);
    345 		}
    346 #endif
    347 
    348 		do_connect(name, sizeof(name), team, enter_status);
    349 #ifdef INTERNET
    350 		if (Send_message != NULL) {
    351 			do_message();
    352 			if (enter_status == Q_MESSAGE)
    353 				break;
    354 			Send_message = NULL;
    355 			/* don't continue as that will call find_driver */
    356 			goto jump_in;
    357 		}
    358 #endif
    359 		playit();
    360 		if ((enter_status = quit(enter_status)) == Q_QUIT)
    361 			break;
    362 	}
    363 	leavex(0, NULL);
    364 	/* NOTREACHED */
    365 	return(0);
    366 }
    367 
    368 #ifdef INTERNET
    369 static void
    370 find_driver(void)
    371 {
    372 	u_short msg;
    373 	const struct sockaddr_storage *host;
    374 	socklen_t hostlen;
    375 	unsigned num;
    376 	int i, c;
    377 
    378 	msg = C_PLAYER;
    379 #ifdef MONITOR
    380 	if (Am_monitor) {
    381 		msg = C_MONITOR;
    382 	}
    383 #endif
    384 
    385 	serverlist_query(msg);
    386 	num = serverlist_num();
    387 	if (num == 0) {
    388 		start_driver();
    389 		sleep(2);
    390 		/* try again */
    391 		serverlist_query(msg);
    392 		num = serverlist_num();
    393 		if (num == 0) {
    394 			/* give up */
    395 			return;
    396 		}
    397 	}
    398 
    399 	if (num == 1) {
    400 		host = serverlist_gethost(0, &hostlen);
    401 	} else {
    402 		clear_the_screen();
    403 		move(1, 0);
    404 		addstr("Pick one:");
    405 		for (i = 0; i < HEIGHT - 4 && i < (int)num; i++) {
    406 			move(3 + i, 0);
    407 			host = serverlist_gethost(i, &hostlen);
    408 			printw("%8c    %.64s", 'a' + i,
    409 			       lookuphost(host, hostlen));
    410 		}
    411 		move(4 + i, 0);
    412 		addstr("Enter letter: ");
    413 		refresh();
    414 		while (1) {
    415 			c = getchar();
    416 			if (c == EOF) {
    417 				leavex(1, "EOF on stdin");
    418 			}
    419 			if (islower((unsigned char)c) && c - 'a' < i) {
    420 				break;
    421 			}
    422 			beep();
    423 			refresh();
    424 		}
    425 		clear_the_screen();
    426 		host = serverlist_gethost(c - 'a', &hostlen);
    427 	}
    428 
    429 	/* XXX fix this (won't work in ipv6) */
    430 	assert(hostlen == sizeof(Daemon));
    431 	memcpy(&Daemon, host, sizeof(Daemon));
    432 }
    433 
    434 static void
    435 dump_scores(const struct sockaddr_storage *host, socklen_t hostlen)
    436 {
    437 	int s;
    438 	char buf[BUFSIZ];
    439 	ssize_t cnt;
    440 
    441 	printf("\n%s:\n", lookuphost(host, hostlen));
    442 	fflush(stdout);
    443 
    444 	s = socket(host->ss_family, SOCK_STREAM, 0);
    445 	if (s < 0)
    446 		err(1, "socket");
    447 	if (connect(s, (const struct sockaddr *)host, hostlen) < 0)
    448 		err(1, "connect");
    449 	while ((cnt = read(s, buf, BUFSIZ)) > 0)
    450 		write(fileno(stdout), buf, cnt);
    451 	(void) close(s);
    452 }
    453 
    454 #endif
    455 
    456 static void
    457 start_driver(void)
    458 {
    459 	int procid;
    460 
    461 #ifdef MONITOR
    462 	if (Am_monitor) {
    463 		leavex(1, "No one playing.");
    464 		/* NOTREACHED */
    465 	}
    466 #endif
    467 
    468 #ifdef INTERNET
    469 	if (contacthost != NULL) {
    470 		sleep(3);
    471 		return;
    472 	}
    473 #endif
    474 
    475 	move(HEIGHT, 0);
    476 	addstr("Starting...");
    477 	refresh();
    478 	procid = fork();
    479 	if (procid == -1) {
    480 		leave(1, "fork failed.");
    481 	}
    482 	if (procid == 0) {
    483 		(void) signal(SIGINT, SIG_IGN);
    484 #ifndef INTERNET
    485 		(void) close(huntsocket);
    486 #else
    487 		if (contactportstr == NULL)
    488 #endif
    489 			execl(Driver, "HUNT", (char *) NULL);
    490 #ifdef INTERNET
    491 		else
    492 			execl(Driver, "HUNT", "-p", contactportstr,
    493 			      (char *) NULL);
    494 #endif
    495 		/* only get here if exec failed */
    496 		(void) kill(getppid(), SIGUSR1);	/* tell mom */
    497 		_exit(1);
    498 	}
    499 	move(HEIGHT, 0);
    500 	addstr("Connecting...");
    501 	refresh();
    502 }
    503 
    504 /*
    505  * bad_con:
    506  *	We had a bad connection.  For the moment we assume that this
    507  *	means the game is full.
    508  */
    509 void
    510 bad_con(void)
    511 {
    512 	leavex(1, "The game is full.  Sorry.");
    513 	/* NOTREACHED */
    514 }
    515 
    516 /*
    517  * bad_ver:
    518  *	version number mismatch.
    519  */
    520 void
    521 bad_ver(void)
    522 {
    523 	leavex(1, "Version number mismatch. No go.");
    524 	/* NOTREACHED */
    525 }
    526 
    527 /*
    528  * sigterm:
    529  *	Handle a terminate signal
    530  */
    531 static void
    532 sigterm(int dummy __unused)
    533 {
    534 	leavex(0, NULL);
    535 	/* NOTREACHED */
    536 }
    537 
    538 
    539 /*
    540  * sigusr1:
    541  *	Handle a usr1 signal
    542  */
    543 static void
    544 sigusr1(int dummy __unused)
    545 {
    546 	leavex(1, "Unable to start driver.  Try again.");
    547 	/* NOTREACHED */
    548 }
    549 
    550 /*
    551  * rmnl:
    552  *	Remove a '\n' at the end of a string if there is one
    553  */
    554 static void
    555 rmnl(char *s)
    556 {
    557 	char *cp;
    558 
    559 	cp = strrchr(s, '\n');
    560 	if (cp != NULL)
    561 		*cp = '\0';
    562 }
    563 
    564 /*
    565  * intr:
    566  *	Handle a interrupt signal
    567  */
    568 void
    569 intr(int dummy __unused)
    570 {
    571 	int ch;
    572 	bool explained;
    573 	int y, x;
    574 
    575 	(void) signal(SIGINT, SIG_IGN);
    576 	getyx(stdscr, y, x);
    577 	move(HEIGHT, 0);
    578 	addstr("Really quit? ");
    579 	clrtoeol();
    580 	refresh();
    581 	explained = false;
    582 	for (;;) {
    583 		ch = getchar();
    584 		if (isupper(ch))
    585 			ch = tolower(ch);
    586 		if (ch == 'y') {
    587 			if (huntsocket != 0) {
    588 				(void) write(huntsocket, "q", 1);
    589 				(void) close(huntsocket);
    590 			}
    591 			leavex(0, NULL);
    592 		}
    593 		else if (ch == 'n') {
    594 			(void) signal(SIGINT, intr);
    595 			move(y, x);
    596 			refresh();
    597 			return;
    598 		}
    599 		if (!explained) {
    600 			addstr("(Yes or No) ");
    601 			refresh();
    602 			explained = true;
    603 		}
    604 		beep();
    605 		refresh();
    606 	}
    607 }
    608 
    609 static void
    610 fincurs(void)
    611 {
    612 	if (in_visual) {
    613 		move(HEIGHT, 0);
    614 		refresh();
    615 		endwin();
    616 	}
    617 }
    618 
    619 /*
    620  * leave:
    621  *	Leave the game somewhat gracefully, restoring all current
    622  *	tty stats, and print errno.
    623  */
    624 void
    625 leave(int exitval, const char *fmt, ...)
    626 {
    627 	int serrno = errno;
    628 	va_list ap;
    629 
    630 	fincurs();
    631 	va_start(ap, fmt);
    632 	errno = serrno;
    633 	verr(exitval, fmt, ap);
    634 	va_end(ap);
    635 }
    636 
    637 /*
    638  * leavex:
    639  *	Leave the game somewhat gracefully, restoring all current
    640  *	tty stats.
    641  */
    642 void
    643 leavex(int exitval, const char *fmt, ...)
    644 {
    645 	va_list ap;
    646 
    647 	fincurs();
    648 	va_start(ap, fmt);
    649 	verrx(exitval, fmt, ap);
    650 	va_end(ap);
    651 }
    652 
    653 static int
    654 env_init(int enter_status)
    655 {
    656 	int i;
    657 	char *envp, *envname, *s;
    658 
    659 	for (i = 0; i < 256; i++)
    660 		map_key[i] = (char) i;
    661 
    662 	envname = NULL;
    663 	if ((envp = getenv("HUNT")) != NULL) {
    664 		while ((s = strpbrk(envp, "=,")) != NULL) {
    665 			if (strncmp(envp, "cloak,", s - envp + 1) == 0) {
    666 				enter_status = Q_CLOAK;
    667 				envp = s + 1;
    668 			}
    669 			else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
    670 				enter_status = Q_SCAN;
    671 				envp = s + 1;
    672 			}
    673 			else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
    674 				enter_status = Q_FLY;
    675 				envp = s + 1;
    676 			}
    677 			else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
    678 				no_beep = true;
    679 				envp = s + 1;
    680 			}
    681 			else if (strncmp(envp, "name=", s - envp + 1) == 0) {
    682 				envname = s + 1;
    683 				if ((s = strchr(envp, ',')) == NULL) {
    684 					*envp = '\0';
    685 					strncpy(name, envname, sizeof(name));
    686 					break;
    687 				}
    688 				*s = '\0';
    689 				strncpy(name, envname, sizeof(name));
    690 				envp = s + 1;
    691 			}
    692 #ifdef INTERNET
    693 			else if (strncmp(envp, "port=", s - envp + 1) == 0) {
    694 				contactportstr = s + 1;
    695 				contactport = atoi(contactportstr);
    696 				if ((s = strchr(envp, ',')) == NULL) {
    697 					*envp = '\0';
    698 					break;
    699 				}
    700 				*s = '\0';
    701 				envp = s + 1;
    702 			}
    703 			else if (strncmp(envp, "host=", s - envp + 1) == 0) {
    704 				contacthost = s + 1;
    705 				if ((s = strchr(envp, ',')) == NULL) {
    706 					*envp = '\0';
    707 					break;
    708 				}
    709 				*s = '\0';
    710 				envp = s + 1;
    711 			}
    712 			else if (strncmp(envp, "message=", s - envp + 1) == 0) {
    713 				Send_message = s + 1;
    714 				if ((s = strchr(envp, ',')) == NULL) {
    715 					*envp = '\0';
    716 					break;
    717 				}
    718 				*s = '\0';
    719 				envp = s + 1;
    720 			}
    721 #endif
    722 			else if (strncmp(envp, "team=", s - envp + 1) == 0) {
    723 				team = *(s + 1);
    724 				if (!isdigit((unsigned char)team))
    725 					team = ' ';
    726 				if ((s = strchr(envp, ',')) == NULL) {
    727 					*envp = '\0';
    728 					break;
    729 				}
    730 				*s = '\0';
    731 				envp = s + 1;
    732 			}			/* must be last option */
    733 			else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) {
    734 				for (s = s + 1; *s != '\0'; s += 2) {
    735 					map_key[(unsigned int) *s] = *(s + 1);
    736 					if (*(s + 1) == '\0') {
    737 						break;
    738 					}
    739 				}
    740 				*envp = '\0';
    741 				break;
    742 			} else {
    743 				*s = '\0';
    744 				printf("unknown option %s\n", envp);
    745 				if ((s = strchr(envp, ',')) == NULL) {
    746 					*envp = '\0';
    747 					break;
    748 				}
    749 				envp = s + 1;
    750 			}
    751 		}
    752 		if (*envp != '\0') {
    753 			if (envname == NULL)
    754 				strncpy(name, envp, sizeof(name));
    755 			else
    756 				printf("unknown option %s\n", envp);
    757 		}
    758 	}
    759 	return enter_status;
    760 }
    761 
    762 static void
    763 fill_in_blanks(void)
    764 {
    765 	int i;
    766 	char *cp;
    767 
    768 again:
    769 	if (name[0] != '\0') {
    770 		printf("Entering as '%s'", name);
    771 		if (team != ' ')
    772 			printf(" on team %c.\n", team);
    773 		else
    774 			putchar('\n');
    775 	} else {
    776 		printf("Enter your code name: ");
    777 		if (fgets(name, sizeof(name), stdin) == NULL)
    778 			exit(1);
    779 	}
    780 	rmnl(name);
    781 	if (name[0] == '\0') {
    782 		name[0] = '\0';
    783 		printf("You have to have a code name!\n");
    784 		goto again;
    785 	}
    786 	for (cp = name; *cp != '\0'; cp++)
    787 		if (!isprint((unsigned char)*cp)) {
    788 			name[0] = '\0';
    789 			printf("Illegal character in your code name.\n");
    790 			goto again;
    791 		}
    792 	if (team == ' ') {
    793 		printf("Enter your team (0-9 or nothing): ");
    794 		i = getchar();
    795 		if (isdigit(i))
    796 			team = i;
    797 		while (i != '\n' && i != EOF)
    798 			i = getchar();
    799 	}
    800 }
    801