xref: /netbsd-src/games/hunt/hunt/hunt.c (revision eec6f7013ae138d067c822f11347980ffbeb2378)
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 *
lookuphost(const struct sockaddr_storage * host,socklen_t hostlen)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
main(int ac,char ** av)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
find_driver(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
dump_scores(const struct sockaddr_storage * host,socklen_t hostlen)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
start_driver(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
bad_con(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
bad_ver(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
sigterm(int dummy __unused)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
sigusr1(int dummy __unused)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
rmnl(char * s)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
intr(int dummy __unused)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
fincurs(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
leave(int exitval,const char * fmt,...)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
leavex(int exitval,const char * fmt,...)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
env_init(int enter_status)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
fill_in_blanks(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