Merge branch 'vendor/GCC50' - gcc 5.0 snapshot 1 FEB 2015
[dragonfly.git] / games / hunt / hunt / hunt.c
1 /*-
2  * Copyright (c) 1983-2003, Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
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.
18  *
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.
30  *
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 $
33  */
34
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>
44
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>
51
52 #include <netinet/in.h>
53 #include <net/if.h>
54
55 #include <arpa/inet.h>
56
57 #include "hunt.h"
58 #include "display.h"
59 #include "client.h"
60 #include "list.h"
61
62 #ifndef __GNUC__
63 #define __attribute__(x)
64 #endif
65
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;
71
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;
77
78
79 static char     name[NAMELEN];
80 static char     team = '-';
81
82 static int      in_visual;
83
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);
90
91 /*
92  * main:
93  *      Main program for local process
94  */
95 int
96 main(int ac, char **av)
97 {
98         int             c;
99         long            enter_status;
100         int             option;
101         struct servent  *se;
102
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                         (void) 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 = '-';
115                         }
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);
157                 }
158         }
159         if (optind + 1 < ac)
160                 goto usage;
161         else if (optind + 1 == ac)
162                 Sock_host = av[ac - 1];
163
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;
170         }
171
172         if (Show_scores) {
173                 dump_scores();
174                 exit(0);
175         }
176
177         if (Query_driver) {
178                 struct driver           *driver;
179
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;
188                 }
189                 exit(0);
190         }
191         if (Otto_mode) {
192                 if (Am_monitor)
193                         errx(1, "otto mode incompatible with monitor mode");
194                 (void) strlcpy(name, "otto", sizeof name);
195                 team = ' ';
196         } else
197                 fill_in_blanks();
198
199         (void) 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");
205         }
206         display_clear_the_screen();
207         (void) signal(SIGINT, intr);
208         (void) signal(SIGTERM, sigterm);
209         /* (void) signal(SIGPIPE, SIG_IGN); */
210
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");
217                 }
218
219                 if (Sock_host == NULL) {
220                         errno = 0;
221                         leave(1, "huntd not running");
222                 }
223
224                 sleep(3);
225         }
226         Socket = -1;
227
228         for (;;) {
229                 if (Socket != -1)
230                         close(Socket);
231
232                 Socket = socket(Daemon.sa_family, SOCK_STREAM, 0);
233                 if (Socket < 0)
234                         leave(1, "socket");
235
236                 option = 1;
237                 if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
238                     &option, sizeof option) < 0)
239                         warn("setsockopt loopback");
240
241                 errno = 0;
242                 if (connect(Socket, &Daemon, Daemon.sa_len) == -1)  {
243                         if (errno == ECONNREFUSED)
244                                 goto ask_driver;
245                         leave(1, "connect");
246                 }
247
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;
255                 }
256                 playit();
257                 if ((enter_status = quit(enter_status)) == Q_QUIT)
258                         break;
259         }
260         leave(0, NULL);
261         /* NOTREACHED */
262         return(0);
263 }
264
265 /*
266  * Set Daemon to be the address of a hunt driver, or return 0 on failure.
267  *
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.
271  */
272 static int
273 find_driver(void)
274 {
275         int last_driver, numdrivers, waiting, is_current;
276         struct driver *driver;
277         int c;
278         char buf[80];
279         const char *xname;
280
281         probe_drivers(Am_monitor ? C_MONITOR : C_PLAYER, Sock_host);
282
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;
299                         }
300                         if (c < 'a' || c >= numdrivers + 'a') {
301                                 display_beep();
302                                 continue;
303                         }
304                         driver = &drivers[c - 'a'];
305                         break;
306                 }
307
308                 if (driver == NULL) {
309                         waiting = 0;
310                         if (numdrivers == 0) {
311                                 probe_cleanup();
312                                 return 0;       /* Failure */
313                         }
314                         if (numdrivers == 1) {
315                                 driver = &drivers[0];
316                                 break;
317                         }
318                         continue;
319                 }
320
321                 /* Use the preferred host straight away. */
322                 if (Sock_host)
323                         break;
324
325                 if (numdrivers == 0) {
326                         display_clear_the_screen();
327                         display_move(1, 0);
328                         display_put_str("Pick one:");
329                 }
330
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;
336
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);
344                 }
345
346                 /* Clear the last 'Enter letter' line if any */
347                 display_move(4 + numdrivers, 0);
348                 display_clear_eol();
349
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: ");
355
356                 display_move(5 + numdrivers, 0);
357                 display_put_str(buf);
358                 display_refresh();
359
360                 numdrivers++;
361         }
362
363         display_clear_the_screen();
364         Daemon = driver->addr;
365
366         probe_cleanup();
367         return 1;               /* Success */
368 }
369
370 static void
371 dump_scores(void)
372 {
373         struct  driver *driver;
374         int     s, cnt, i;
375         char    buf[1024];
376
377         probe_drivers(C_SCORES, Sock_host);
378         while ((driver = next_driver()) != NULL) {
379                 printf("\n%s:\n", driver_name(driver));
380                 fflush(stdout);
381
382                 if ((s = socket(driver->addr.sa_family, SOCK_STREAM, 0)) < 0) {
383                         warn("socket");
384                         continue;
385                 }
386                 if (connect(s, &driver->addr, driver->addr.sa_len) < 0) {
387                         warn("connect");
388                         close(s);
389                         continue;
390                 }
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);
398                 }
399                 if (cnt < 0)
400                         warn("read");
401                 (void)close(s);
402                 if (Sock_host)
403                         break;
404         }
405         probe_cleanup();
406 }
407
408
409 /*
410  * bad_con:
411  *      We had a bad connection.  For the moment we assume that this
412  *      means the game is full.
413  */
414 void
415 bad_con(void)
416 {
417         leave(1, "lost connection to huntd");
418 }
419
420 /*
421  * bad_ver:
422  *      version number mismatch.
423  */
424 void
425 bad_ver(void)
426 {
427         errno = 0;
428         leave(1, "Version number mismatch. No go.");
429 }
430
431 /*
432  * sigterm:
433  *      Handle a terminate signal
434  */
435 static void
436 sigterm(int signo __unused)
437 {
438         leave(0, NULL);
439 }
440
441 /*
442  * rmnl:
443  *      Remove a '\n' at the end of a string if there is one
444  */
445 static void
446 rmnl(char *s)
447 {
448         char    *cp;
449
450         cp = strrchr(s, '\n');
451         if (cp != NULL)
452                 *cp = '\0';
453 }
454
455 /*
456  * intr:
457  *      Handle a interrupt signal
458  */
459 void
460 intr(int dummy __unused)
461 {
462         int     ch;
463         int     explained;
464         int     y, x;
465
466         (void) 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                                 (void) write(Socket, "q", 1);
480                                 (void) close(Socket);
481                         }
482                         leave(0, NULL);
483                 }
484                 else if (ch == 'n') {
485                         (void) signal(SIGINT, intr);
486                         display_move(y, x);
487                         display_refresh();
488                         return;
489                 }
490                 if (!explained) {
491                         display_put_str("(Yes or No) ");
492                         display_refresh();
493                         explained = TRUE;
494                 }
495                 display_beep();
496                 display_refresh();
497         }
498 }
499
500 /*
501  * leave:
502  *      Leave the game somewhat gracefully, restoring all current
503  *      tty stats.
504  */
505 static void
506 leave(int eval, const char *mesg)
507 {
508         int saved_errno;
509
510         saved_errno = errno;
511         if (in_visual) {
512                 display_move(HEIGHT, 0);
513                 display_refresh();
514                 display_end();
515         }
516         errno = saved_errno;
517
518         if (errno == 0 && mesg != NULL)
519                 errx(eval, "%s", mesg);
520         else if (mesg != NULL)
521                 err(eval, "%s", mesg);
522         exit(eval);
523 }
524
525 /*
526  * env_init:
527  *      initialise game parameters from the HUNT envvar
528  */
529 static long
530 env_init(long enter_status)
531 {
532         int     i;
533         char    *envp, *envname, *s;
534
535         /* Map all keys to themselves: */
536         for (i = 0; i < 256; i++)
537                 map_key[i] = (char) i;
538
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;
545                         }
546                         else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
547                                 enter_status = Q_SCAN;
548                                 envp = s + 1;
549                         }
550                         else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
551                                 enter_status = Q_FLY;
552                                 envp = s + 1;
553                         }
554                         else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
555                                 no_beep = TRUE;
556                                 envp = s + 1;
557                         }
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;
564                                 }
565                                 *s = '\0';
566                                 strlcpy(name, envname, sizeof name);
567                                 envp = s + 1;
568                         }
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;
575                                 }
576                                 *s = '\0';
577                                 envp = s + 1;
578                         }
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;
584                                 }
585                                 *s = '\0';
586                                 envp = s + 1;
587                         }
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;
593                                 }
594                                 *s = '\0';
595                                 envp = s + 1;
596                         }
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;
604                                 }
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;
613                                         }
614                                 }
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;
623                                 }
624                                 envp = s + 1;
625                         }
626                 }
627                 if (*envp != '\0') {
628                         if (envname == NULL)
629                                 strlcpy(name, envp, sizeof name);
630                         else
631                                 printf("unknown option %s\n", envp);
632                 }
633         }
634         return enter_status;
635 }
636
637 /*
638  * fill_in_blanks:
639  *      quiz the user for the information they didn't provide earlier
640  */
641 static void
642 fill_in_blanks(void)
643 {
644         int     i;
645         char    *cp;
646
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);
658         }
659         rmnl(name);
660         if (name[0] == '\0') {
661                 printf("You have to have a code name!\n");
662                 goto again;
663         }
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;
669                 }
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;
683                 }
684         }
685 }