games: Remove (void) casts.
[dragonfly.git] / games / hunt / hunt / hunt.c
blobbb412114876549435df5882b3ec4dd1ea4dec3ed
1 /*-
2 * Copyright (c) 1983-2003, Regents of the University of California.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University of California, San Francisco nor
15 * the names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * $OpenBSD: hunt.c,v 1.13 2008/03/17 09:17:56 sobrado Exp $
32 * $NetBSD: hunt.c,v 1.8 1998/09/13 15:27:28 hubertf Exp $
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <curses.h>
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <netdb.h>
45 #include <sys/stat.h>
46 #include <sys/time.h>
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <sys/ioctl.h>
50 #include <sys/sockio.h>
52 #include <netinet/in.h>
53 #include <net/if.h>
55 #include <arpa/inet.h>
57 #include "hunt.h"
58 #include "display.h"
59 #include "client.h"
60 #include "list.h"
62 #ifndef __GNUC__
63 #define __attribute__(x)
64 #endif
66 FLAG Am_monitor = FALSE;
67 int Socket;
68 char map_key[256]; /* what to map keys to */
69 FLAG no_beep = FALSE;
70 char *Send_message = NULL;
72 static char *Sock_host;
73 static char *use_port;
74 static FLAG Query_driver = FALSE;
75 static FLAG Show_scores = FALSE;
76 static struct sockaddr Daemon;
79 static char name[NAMELEN];
80 static char team = '-';
82 static int in_visual;
84 static void dump_scores(void);
85 static long env_init(long);
86 static void fill_in_blanks(void);
87 static void leave(int, const char *) __attribute__((__noreturn__));
88 static void sigterm(int);
89 static int find_driver(void);
92 * main:
93 * Main program for local process
95 int
96 main(int ac, char **av)
98 int c;
99 long enter_status;
100 int option;
101 struct servent *se;
103 enter_status = env_init((long) Q_CLOAK);
104 while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != -1) {
105 switch (c) {
106 case 'l': /* rsh compatibility */
107 case 'n':
108 strlcpy(name, optarg, sizeof name);
109 break;
110 case 't':
111 team = *optarg;
112 if (!isdigit(team) && team != ' ') {
113 warnx("Team names must be numeric or space");
114 team = '-';
116 break;
117 case 'o':
118 Otto_mode = TRUE;
119 break;
120 case 'm':
121 Am_monitor = TRUE;
122 break;
123 case 'S':
124 Show_scores = TRUE;
125 break;
126 case 'q': /* query whether hunt is running */
127 Query_driver = TRUE;
128 break;
129 case 'w':
130 Send_message = optarg;
131 break;
132 case 'h':
133 Sock_host = optarg;
134 break;
135 case 'p':
136 use_port = optarg;
137 Server_port = atoi(use_port);
138 break;
139 case 'c':
140 enter_status = Q_CLOAK;
141 break;
142 case 'f':
143 enter_status = Q_FLY;
144 break;
145 case 's':
146 enter_status = Q_SCAN;
147 break;
148 case 'b':
149 no_beep = !no_beep;
150 break;
151 default:
152 usage:
153 fputs("usage: hunt [-bcfmqSs] [-n name] [-p port] "
154 "[-t team] [-w message] [[-h] host]\n",
155 stderr);
156 exit(1);
159 if (optind + 1 < ac)
160 goto usage;
161 else if (optind + 1 == ac)
162 Sock_host = av[ac - 1];
164 if (Server_port == 0) {
165 se = getservbyname("hunt", "udp");
166 if (se != NULL)
167 Server_port = ntohs(se->s_port);
168 else
169 Server_port = HUNT_PORT;
172 if (Show_scores) {
173 dump_scores();
174 exit(0);
177 if (Query_driver) {
178 struct driver *driver;
180 probe_drivers(C_MESSAGE, Sock_host);
181 while ((driver = next_driver()) != NULL) {
182 printf("%d player%s hunting on %s!\n",
183 driver->response,
184 (driver->response == 1) ? "" : "s",
185 driver_name(driver));
186 if (Sock_host)
187 break;
189 exit(0);
191 if (Otto_mode) {
192 if (Am_monitor)
193 errx(1, "otto mode incompatible with monitor mode");
194 strlcpy(name, "otto", sizeof name);
195 team = ' ';
196 } else
197 fill_in_blanks();
199 fflush(stdout);
200 display_open();
201 in_visual = TRUE;
202 if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH) {
203 errno = 0;
204 leave(1, "Need a larger window");
206 display_clear_the_screen();
207 signal(SIGINT, intr);
208 signal(SIGTERM, sigterm);
209 /* signal(SIGPIPE, SIG_IGN); */
211 Daemon.sa_len = 0;
212 ask_driver:
213 while (!find_driver()) {
214 if (Am_monitor) {
215 errno = 0;
216 leave(1, "No one playing");
219 if (Sock_host == NULL) {
220 errno = 0;
221 leave(1, "huntd not running");
224 sleep(3);
226 Socket = -1;
228 for (;;) {
229 if (Socket != -1)
230 close(Socket);
232 Socket = socket(Daemon.sa_family, SOCK_STREAM, 0);
233 if (Socket < 0)
234 leave(1, "socket");
236 option = 1;
237 if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
238 &option, sizeof option) < 0)
239 warn("setsockopt loopback");
241 errno = 0;
242 if (connect(Socket, &Daemon, Daemon.sa_len) == -1) {
243 if (errno == ECONNREFUSED)
244 goto ask_driver;
245 leave(1, "connect");
248 do_connect(name, team, enter_status);
249 if (Send_message != NULL) {
250 do_message();
251 if (enter_status == Q_MESSAGE)
252 break;
253 Send_message = NULL;
254 continue;
256 playit();
257 if ((enter_status = quit(enter_status)) == Q_QUIT)
258 break;
260 leave(0, NULL);
261 /* NOTREACHED */
262 return(0);
266 * Set Daemon to be the address of a hunt driver, or return 0 on failure.
268 * We start quietly probing for drivers. As soon as one driver is found
269 * we show it in the list. If we run out of drivers and we only have one
270 * then we choose it. Otherwise we present a list of the found drivers.
272 static int
273 find_driver(void)
275 int last_driver, numdrivers, waiting, is_current;
276 struct driver *driver;
277 int c;
278 char buf[80];
279 const char *xname;
281 probe_drivers(Am_monitor ? C_MONITOR : C_PLAYER, Sock_host);
283 last_driver = -1;
284 numdrivers = 0;
285 waiting = 1;
286 for (;;) {
287 if (numdrivers == 0) {
288 /* Silently wait for at least one driver */
289 driver = next_driver();
290 } else if (!waiting || (driver =
291 next_driver_fd(STDIN_FILENO)) == (struct driver *)-1) {
292 /* We have a key waiting, or no drivers left */
293 c = getchar();
294 if (c == '\r' || c == '\n' || c == ' ') {
295 if (numdrivers == 1)
296 c = 'a';
297 else if (last_driver != -1)
298 c = 'a' + last_driver;
300 if (c < 'a' || c >= numdrivers + 'a') {
301 display_beep();
302 continue;
304 driver = &drivers[c - 'a'];
305 break;
308 if (driver == NULL) {
309 waiting = 0;
310 if (numdrivers == 0) {
311 probe_cleanup();
312 return 0; /* Failure */
314 if (numdrivers == 1) {
315 driver = &drivers[0];
316 break;
318 continue;
321 /* Use the preferred host straight away. */
322 if (Sock_host)
323 break;
325 if (numdrivers == 0) {
326 display_clear_the_screen();
327 display_move(1, 0);
328 display_put_str("Pick one:");
331 /* Mark the last driver we used with an asterisk */
332 is_current = (last_driver == -1 && Daemon.sa_len != 0 &&
333 memcmp(&Daemon, &driver->addr, Daemon.sa_len) == 0);
334 if (is_current)
335 last_driver = numdrivers;
337 /* Display it in the list if there is room */
338 if (numdrivers < HEIGHT - 3) {
339 xname = driver_name(driver);
340 display_move(3 + numdrivers, 0);
341 snprintf(buf, sizeof buf, "%6c %c %s",
342 is_current ? '*' : ' ', 'a' + numdrivers, xname);
343 display_put_str(buf);
346 /* Clear the last 'Enter letter' line if any */
347 display_move(4 + numdrivers, 0);
348 display_clear_eol();
350 if (last_driver != -1)
351 snprintf(buf, sizeof buf, "Enter letter [%c]: ",
352 'a' + last_driver);
353 else
354 snprintf(buf, sizeof buf, "Enter letter: ");
356 display_move(5 + numdrivers, 0);
357 display_put_str(buf);
358 display_refresh();
360 numdrivers++;
363 display_clear_the_screen();
364 Daemon = driver->addr;
366 probe_cleanup();
367 return 1; /* Success */
370 static void
371 dump_scores(void)
373 struct driver *driver;
374 int s, cnt, i;
375 char buf[1024];
377 probe_drivers(C_SCORES, Sock_host);
378 while ((driver = next_driver()) != NULL) {
379 printf("\n%s:\n", driver_name(driver));
380 fflush(stdout);
382 if ((s = socket(driver->addr.sa_family, SOCK_STREAM, 0)) < 0) {
383 warn("socket");
384 continue;
386 if (connect(s, &driver->addr, driver->addr.sa_len) < 0) {
387 warn("connect");
388 close(s);
389 continue;
391 while ((cnt = read(s, buf, sizeof buf)) > 0) {
392 /* Whittle out bad characters */
393 for (i = 0; i < cnt; i++)
394 if ((buf[i] < ' ' || buf[i] > '~') &&
395 buf[i] != '\n' && buf[i] != '\t')
396 buf[i] = '?';
397 fwrite(buf, cnt, 1, stdout);
399 if (cnt < 0)
400 warn("read");
401 close(s);
402 if (Sock_host)
403 break;
405 probe_cleanup();
410 * bad_con:
411 * We had a bad connection. For the moment we assume that this
412 * means the game is full.
414 void
415 bad_con(void)
417 leave(1, "lost connection to huntd");
421 * bad_ver:
422 * version number mismatch.
424 void
425 bad_ver(void)
427 errno = 0;
428 leave(1, "Version number mismatch. No go.");
432 * sigterm:
433 * Handle a terminate signal
435 static void
436 sigterm(int signo __unused)
438 leave(0, NULL);
442 * rmnl:
443 * Remove a '\n' at the end of a string if there is one
445 static void
446 rmnl(char *s)
448 char *cp;
450 cp = strrchr(s, '\n');
451 if (cp != NULL)
452 *cp = '\0';
456 * intr:
457 * Handle a interrupt signal
459 void
460 intr(int dummy __unused)
462 int ch;
463 int explained;
464 int y, x;
466 signal(SIGINT, SIG_IGN);
467 display_getyx(&y, &x);
468 display_move(HEIGHT, 0);
469 display_put_str("Really quit? ");
470 display_clear_eol();
471 display_refresh();
472 explained = FALSE;
473 for (;;) {
474 ch = getchar();
475 if (isupper(ch))
476 ch = tolower(ch);
477 if (ch == 'y') {
478 if (Socket != 0) {
479 write(Socket, "q", 1);
480 close(Socket);
482 leave(0, NULL);
484 else if (ch == 'n') {
485 signal(SIGINT, intr);
486 display_move(y, x);
487 display_refresh();
488 return;
490 if (!explained) {
491 display_put_str("(Yes or No) ");
492 display_refresh();
493 explained = TRUE;
495 display_beep();
496 display_refresh();
501 * leave:
502 * Leave the game somewhat gracefully, restoring all current
503 * tty stats.
505 static void
506 leave(int eval, const char *mesg)
508 int saved_errno;
510 saved_errno = errno;
511 if (in_visual) {
512 display_move(HEIGHT, 0);
513 display_refresh();
514 display_end();
516 errno = saved_errno;
518 if (errno == 0 && mesg != NULL)
519 errx(eval, "%s", mesg);
520 else if (mesg != NULL)
521 err(eval, "%s", mesg);
522 exit(eval);
526 * env_init:
527 * initialise game parameters from the HUNT envvar
529 static long
530 env_init(long enter_status)
532 int i;
533 char *envp, *envname, *s;
535 /* Map all keys to themselves: */
536 for (i = 0; i < 256; i++)
537 map_key[i] = (char) i;
539 envname = NULL;
540 if ((envp = getenv("HUNT")) != NULL) {
541 while ((s = strpbrk(envp, "=,")) != NULL) {
542 if (strncmp(envp, "cloak,", s - envp + 1) == 0) {
543 enter_status = Q_CLOAK;
544 envp = s + 1;
546 else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
547 enter_status = Q_SCAN;
548 envp = s + 1;
550 else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
551 enter_status = Q_FLY;
552 envp = s + 1;
554 else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
555 no_beep = TRUE;
556 envp = s + 1;
558 else if (strncmp(envp, "name=", s - envp + 1) == 0) {
559 envname = s + 1;
560 if ((s = strchr(envp, ',')) == NULL) {
561 *envp = '\0';
562 strlcpy(name, envname, sizeof name);
563 break;
565 *s = '\0';
566 strlcpy(name, envname, sizeof name);
567 envp = s + 1;
569 else if (strncmp(envp, "port=", s - envp + 1) == 0) {
570 use_port = s + 1;
571 Server_port = atoi(use_port);
572 if ((s = strchr(envp, ',')) == NULL) {
573 *envp = '\0';
574 break;
576 *s = '\0';
577 envp = s + 1;
579 else if (strncmp(envp, "host=", s - envp + 1) == 0) {
580 Sock_host = s + 1;
581 if ((s = strchr(envp, ',')) == NULL) {
582 *envp = '\0';
583 break;
585 *s = '\0';
586 envp = s + 1;
588 else if (strncmp(envp, "message=", s - envp + 1) == 0) {
589 Send_message = s + 1;
590 if ((s = strchr(envp, ',')) == NULL) {
591 *envp = '\0';
592 break;
594 *s = '\0';
595 envp = s + 1;
597 else if (strncmp(envp, "team=", s - envp + 1) == 0) {
598 team = *(s + 1);
599 if (!isdigit(team))
600 team = ' ';
601 if ((s = strchr(envp, ',')) == NULL) {
602 *envp = '\0';
603 break;
605 *s = '\0';
606 envp = s + 1;
607 } /* must be last option */
608 else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) {
609 for (s = s + 1; *s != '\0'; s += 2) {
610 map_key[(unsigned int) *s] = *(s + 1);
611 if (*(s + 1) == '\0') {
612 break;
615 *envp = '\0';
616 break;
617 } else {
618 *s = '\0';
619 printf("unknown option %s\n", envp);
620 if ((s = strchr(envp, ',')) == NULL) {
621 *envp = '\0';
622 break;
624 envp = s + 1;
627 if (*envp != '\0') {
628 if (envname == NULL)
629 strlcpy(name, envp, sizeof name);
630 else
631 printf("unknown option %s\n", envp);
634 return enter_status;
638 * fill_in_blanks:
639 * quiz the user for the information they didn't provide earlier
641 static void
642 fill_in_blanks(void)
644 int i;
645 char *cp;
647 again:
648 if (name[0] != '\0') {
649 printf("Entering as '%s'", name);
650 if (team != ' ' && team != '-')
651 printf(" on team %c.\n", team);
652 else
653 putchar('\n');
654 } else {
655 printf("Enter your code name: ");
656 if (fgets(name, sizeof name, stdin) == NULL)
657 exit(1);
659 rmnl(name);
660 if (name[0] == '\0') {
661 printf("You have to have a code name!\n");
662 goto again;
664 for (cp = name; *cp != '\0'; cp++)
665 if (!isprint(*cp)) {
666 name[0] = '\0';
667 printf("Illegal character in your code name.\n");
668 goto again;
670 if (team == '-') {
671 printf("Enter your team (0-9 or nothing): ");
672 i = getchar();
673 if (isdigit(i))
674 team = i;
675 else if (i == '\n' || i == EOF || i == ' ')
676 team = ' ';
677 /* ignore trailing chars */
678 while (i != '\n' && i != EOF)
679 i = getchar();
680 if (team == '-') {
681 printf("Teams must be numeric.\n");
682 goto again;