xref: /dragonfly/games/atc/input.c (revision d9f85b3315fdf3905eee2057d79dd723366a8bf6)
1 /*-
2  * Copyright (c) 1990, 1993
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Ed James.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)input.c      8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/atc/input.c,v 1.7 2000/02/27 23:02:47 mph Exp $
34  */
35 
36 /*
37  * Copyright (c) 1987 by Ed James, UC Berkeley.  All rights reserved.
38  *
39  * Copy permission is hereby granted provided that this notice is
40  * retained on all partial or complete copies.
41  *
42  * For more info on this and all of my stuff, mail edjames@berkeley.edu.
43  */
44 
45 #include <sys/wait.h>
46 
47 #include <stdlib.h>
48 #include <string.h>
49 #include "include.h"
50 #include "pathnames.h"
51 
52 #define MAXRULES    6
53 #define MAXDEPTH    15
54 
55 #define RETTOKEN    '\r'
56 #define REDRAWTOKEN '\014'    /* CTRL(L) */
57 #define   SHELLTOKEN          '!'
58 #define HELPTOKEN   '?'
59 #define ALPHATOKEN  256
60 #define NUMTOKEN    257
61 
62 typedef struct {
63           int                 token;
64           int                 to_state;
65           const char          *str;
66           const char          *(*func)(int);
67 } RULE;
68 
69 typedef struct {
70           int       num_rules;
71           RULE      *rule;
72 } STATE;
73 
74 typedef struct {
75           char      str[20];
76           int       state;
77           int       rule;
78           int       ch;
79           int       pos;
80 } STACK;
81 
82 #define T_RULE                stack[level].rule
83 #define T_STATE               stack[level].state
84 #define T_STR                 stack[level].str
85 #define T_POS                 stack[level].pos
86 #define   T_CH                stack[level].ch
87 
88 #define NUMELS(a)   (sizeof (a) / sizeof (*(a)))
89 
90 #define NUMSTATES   NUMELS(st)
91 
92 static int          pop(void);
93 static void         rezero(void);
94 static void         push(int, int);
95 static void         noise(void);
96 static int          gettoken(void);
97 static const char   *setplane(int);
98 static const char   *turn(int);
99 static const char   *circle(int);
100 static const char   *left(int);
101 static const char   *right(int);
102 static const char   *Left(int);
103 static const char   *Right(int);
104 static const char   *delayb(int);
105 static const char   *beacon(int);
106 static const char   *ex_it(int);
107 static const char   *airport(int);
108 static const char   *climb(int);
109 static const char   *descend(int);
110 static const char   *setalt(int);
111 static const char   *setrelalt(int);
112 static const char   *benum(int);
113 static const char   *to_dir(int);
114 static const char   *rel_dir(int);
115 static const char   *mark(int);
116 static const char   *unmark(int);
117 static const char   *ignore(int);
118 
119 RULE      state0[] = {        { ALPHATOKEN,       1,        "%c:",              setplane},
120                               { RETTOKEN,         -1,       "",                 NULL      },
121                               { HELPTOKEN,        12,       " [a-z]<ret>",      NULL      }},
122           state1[] = {        { 't',              2,        " turn",  turn      },
123                               { 'a',              3,        " altitude:",       NULL      },
124                               { 'c',              4,        " circle",          circle    },
125                               { 'm',              7,        " mark",  mark      },
126                               { 'u',              7,        " unmark",          unmark    },
127                               { 'i',              7,        " ignore",          ignore    },
128                               { HELPTOKEN,        12,       " tacmui",          NULL      }},
129           state2[] = {        { 'l',              6,        " left",  left      },
130                               { 'r',              6,        " right", right     },
131                               { 'L',              4,        " left 90",         Left      },
132                               { 'R',              4,        " right 90",        Right     },
133                               { 't',              11,       " towards",         NULL      },
134                               { 'w',              4,        " to 0",  to_dir    },
135                               { 'e',              4,        " to 45", to_dir    },
136                               { 'd',              4,        " to 90", to_dir    },
137                               { 'c',              4,        " to 135",          to_dir    },
138                               { 'x',              4,        " to 180",          to_dir    },
139                               { 'z',              4,        " to 225",          to_dir    },
140                               { 'a',              4,        " to 270",          to_dir    },
141                               { 'q',              4,        " to 315",          to_dir    },
142                               { HELPTOKEN,        12,       " lrLRt<dir>",      NULL      }},
143           state3[] = {        { '+',              10,       " climb", climb     },
144                               { 'c',              10,       " climb", climb     },
145                               { '-',              10,       " descend",         descend   },
146                               { 'd',              10,       " descend",         descend   },
147                               { NUMTOKEN,         7,        " %c000 feet",      setalt    },
148                               { HELPTOKEN,        12,       " +-cd[0-9]",       NULL      }},
149           state4[] = {        { '@',              9,        " at",              NULL      },
150                               { 'a',              9,        " at",              NULL      },
151                               { RETTOKEN,         -1,       "",                 NULL      },
152                               { HELPTOKEN,        12,       " @a<ret>",         NULL      }},
153           state5[] = {        { NUMTOKEN,         7,        "%c",               delayb    },
154                               { HELPTOKEN,        12,       " [0-9]", NULL      }},
155           state6[] = {        { '@',              9,        " at",              NULL      },
156                               { 'a',              9,        " at",              NULL      },
157                               { 'w',              4,        " 0",               rel_dir   },
158                               { 'e',              4,        " 45",              rel_dir   },
159                               { 'd',              4,        " 90",              rel_dir   },
160                               { 'c',              4,        " 135",             rel_dir   },
161                               { 'x',              4,        " 180",             rel_dir   },
162                               { 'z',              4,        " 225",             rel_dir   },
163                               { 'a',              4,        " 270",             rel_dir   },
164                               { 'q',              4,        " 315",             rel_dir   },
165                               { RETTOKEN,         -1,       "",                 NULL      },
166                               { HELPTOKEN,        12,       " @a<dir><ret>",NULL          }},
167           state7[] = {        { RETTOKEN,         -1,       "",                 NULL      },
168                               { HELPTOKEN,        12,       " <ret>", NULL      }},
169           state8[] = {        { NUMTOKEN,         4,        "%c",               benum     },
170                               { HELPTOKEN,        12,       " [0-9]", NULL      }},
171           state9[] = {        { 'b',              5,        " beacon #",        NULL      },
172                               { '*',              5,        " beacon #",        NULL      },
173                               { HELPTOKEN,        12,       " b*",              NULL      }},
174           state10[] = {       { NUMTOKEN,         7,        " %c000 ft",        setrelalt},
175                               { HELPTOKEN,        12,       " [0-9]", NULL      }},
176           state11[] = {       { 'b',              8,        " beacon #",        beacon    },
177                               { '*',              8,        " beacon #",        beacon    },
178                               { 'e',              8,        " exit #",          ex_it     },
179                               { 'a',              8,        " airport #",       airport   },
180                               { HELPTOKEN,        12,       " b*ea",  NULL      }},
181           state12[] = {       { -1,               -1,       "",                 NULL      }};
182 
183 #define DEF_STATE(s)          { NUMELS(s),        (s)       }
184 
185 STATE     st[] = {
186           DEF_STATE(state0), DEF_STATE(state1), DEF_STATE(state2),
187           DEF_STATE(state3), DEF_STATE(state4), DEF_STATE(state5),
188           DEF_STATE(state6), DEF_STATE(state7), DEF_STATE(state8),
189           DEF_STATE(state9), DEF_STATE(state10), DEF_STATE(state11),
190           DEF_STATE(state12)
191 };
192 
193 PLANE     p;
194 STACK     stack[MAXDEPTH];
195 int       level;
196 int       tval;
197 int       dest_type, dest_no, dir;
198 
199 static int
pop(void)200 pop(void)
201 {
202           if (level == 0)
203                     return (-1);
204           level--;
205 
206           ioclrtoeol(T_POS);
207 
208           strcpy(T_STR, "");
209           T_RULE = -1;
210           T_CH = -1;
211           return (0);
212 }
213 
214 static void
rezero(void)215 rezero(void)
216 {
217           iomove(0);
218 
219           level = 0;
220           T_STATE = 0;
221           T_RULE = -1;
222           T_CH = -1;
223           T_POS = 0;
224           strcpy(T_STR, "");
225 }
226 
227 static void
push(int ruleno,int ch)228 push(int ruleno, int ch)
229 {
230           int       newstate, newpos;
231 
232           sprintf(T_STR, st[T_STATE].rule[ruleno].str, tval);
233           T_RULE = ruleno;
234           T_CH = ch;
235           newstate = st[T_STATE].rule[ruleno].to_state;
236           newpos = T_POS + strlen(T_STR);
237 
238           ioaddstr(T_POS, T_STR);
239 
240           if (level == 0)
241                     ioclrtobot();
242           level++;
243           T_STATE = newstate;
244           T_POS = newpos;
245           T_RULE = -1;
246           strcpy(T_STR, "");
247 }
248 
249 int
getcommand(void)250 getcommand(void)
251 {
252           int                 c, i, done;
253           const char          *s, *(*func)(int);
254           PLANE               *pp;
255 
256           rezero();
257 
258           do {
259                     c = gettoken();
260                     if (c == tty_new.c_cc[VERASE]) {
261                               if (pop() < 0)
262                                         noise();
263                     } else if (c == tty_new.c_cc[VKILL]) {
264                               while (pop() >= 0)
265                                         ;
266                     } else {
267                               done = 0;
268                               for (i = 0; i < st[T_STATE].num_rules; i++) {
269                                         if (st[T_STATE].rule[i].token == c ||
270                                             st[T_STATE].rule[i].token == tval) {
271                                                   push(i, (c >= ALPHATOKEN) ? tval : c);
272                                                   done = 1;
273                                                   break;
274                                         }
275                               }
276                               if (!done)
277                                         noise();
278                     }
279           } while (T_STATE != -1);
280 
281           if (level == 1)
282                     return (1);         /* forced update */
283 
284           dest_type = T_NODEST;
285 
286           for (i = 0; i < level; i++) {
287                     func = st[stack[i].state].rule[stack[i].rule].func;
288                     if (func != NULL)
289                               if ((s = (*func)(stack[i].ch)) != NULL) {
290                                         ioerror(stack[i].pos, strlen(stack[i].str), s);
291                                         return (-1);
292                               }
293           }
294 
295           pp = findplane(p.plane_no);
296           if (pp->new_altitude != p.new_altitude)
297                     pp->new_altitude = p.new_altitude;
298           else if (pp->status != p.status)
299                     pp->status = p.status;
300           else {
301                     pp->new_dir = p.new_dir;
302                     pp->delayd = p.delayd;
303                     pp->delayd_no = p.delayd_no;
304           }
305           return (0);
306 }
307 
308 static void
noise(void)309 noise(void)
310 {
311           putchar('\07');
312           fflush(stdout);
313 }
314 
315 static int
gettoken(void)316 gettoken(void)
317 {
318           while ((tval = getAChar()) == REDRAWTOKEN || tval == SHELLTOKEN)
319           {
320                     if (tval == SHELLTOKEN)
321                     {
322                               struct itimerval    itv;
323                               itv.it_value.tv_sec = 0;
324                               itv.it_value.tv_usec = 0;
325                               setitimer(ITIMER_REAL, &itv, NULL);
326                               if (fork() == 0)    /* child */
327                               {
328                                         char *shell, *base;
329 
330                                         /* revoke */
331                                         setgid(getgid());
332                                         done_screen();
333 
334                                                              /* run user's favorite shell */
335                                         if ((shell = getenv("SHELL")) != NULL)
336                                         {
337                                                   base = strrchr(shell, '/');
338                                                   if (base == NULL)
339                                                             base = shell;
340                                                   else
341                                                             base++;
342                                                   execl(shell, base, NULL);
343                                         }
344                                         else
345                                                   execl(_PATH_BSHELL, "sh", NULL);
346 
347                                         exit(0);  /* oops */
348                               }
349 
350                               wait(0);
351                               tcsetattr(fileno(stdin), TCSANOW, &tty_new);
352                               itv.it_value.tv_sec = 0;
353                               itv.it_value.tv_usec = 1;
354                               itv.it_interval.tv_sec = sp->update_secs;
355                               itv.it_interval.tv_usec = 0;
356                               setitimer(ITIMER_REAL, &itv, NULL);
357                     }
358                     redraw();
359           }
360 
361           if (isdigit(tval))
362                     return (NUMTOKEN);
363           else if (isalpha(tval))
364                     return (ALPHATOKEN);
365           else
366                     return (tval);
367 }
368 
369 static const char *
setplane(int c)370 setplane(int c)
371 {
372           PLANE     *pp;
373 
374           pp = findplane(number(c));
375           if (pp == NULL)
376                     return ("Unknown Plane");
377           bcopy(pp, &p, sizeof (p));
378           p.delayd = 0;
379           return (NULL);
380 }
381 
382 static const char *
turn(int c __unused)383 turn(int c __unused)
384 {
385           if (p.altitude == 0)
386                     return ("Planes at airports may not change direction");
387           return (NULL);
388 }
389 
390 static const char *
circle(int c __unused)391 circle(int c __unused)
392 {
393           if (p.altitude == 0)
394                     return ("Planes cannot circle on the ground");
395           p.new_dir = MAXDIR;
396           return (NULL);
397 }
398 
399 static const char *
left(int c __unused)400 left(int c __unused)
401 {
402           dir = D_LEFT;
403           p.new_dir = p.dir - 1;
404           if (p.new_dir < 0)
405                     p.new_dir += MAXDIR;
406           return (NULL);
407 }
408 
409 static const char *
right(int c __unused)410 right(int c __unused)
411 {
412           dir = D_RIGHT;
413           p.new_dir = p.dir + 1;
414           if (p.new_dir >= MAXDIR)
415                     p.new_dir -= MAXDIR;
416           return (NULL);
417 }
418 
419 static const char *
Left(int c __unused)420 Left(int c __unused)
421 {
422           p.new_dir = p.dir - 2;
423           if (p.new_dir < 0)
424                     p.new_dir += MAXDIR;
425           return (NULL);
426 }
427 
428 static const char *
Right(int c __unused)429 Right(int c __unused)
430 {
431           p.new_dir = p.dir + 2;
432           if (p.new_dir >= MAXDIR)
433                     p.new_dir -= MAXDIR;
434           return (NULL);
435 }
436 
437 static const char *
delayb(int c)438 delayb(int c)
439 {
440           int       xdiff, ydiff;
441 
442           c -= '0';
443 
444           if (c >= sp->num_beacons)
445                     return ("Unknown beacon");
446           xdiff = sp->beacon[c].x - p.xpos;
447           xdiff = SGN(xdiff);
448           ydiff = sp->beacon[c].y - p.ypos;
449           ydiff = SGN(ydiff);
450           if (xdiff != displacement[p.dir].dx || ydiff != displacement[p.dir].dy)
451                     return ("Beacon is not in flight path");
452           p.delayd = 1;
453           p.delayd_no = c;
454 
455           if (dest_type != T_NODEST) {
456                     switch (dest_type) {
457                     case T_BEACON:
458                               xdiff = sp->beacon[dest_no].x - sp->beacon[c].x;
459                               ydiff = sp->beacon[dest_no].y - sp->beacon[c].y;
460                               break;
461                     case T_EXIT:
462                               xdiff = sp->exit[dest_no].x - sp->beacon[c].x;
463                               ydiff = sp->exit[dest_no].y - sp->beacon[c].y;
464                               break;
465                     case T_AIRPORT:
466                               xdiff = sp->airport[dest_no].x - sp->beacon[c].x;
467                               ydiff = sp->airport[dest_no].y - sp->beacon[c].y;
468                               break;
469                     default:
470                               return ("Bad case in delayb!  Get help!");
471                               break;
472                     }
473                     if (xdiff == 0 && ydiff == 0)
474                               return ("Would already be there");
475                     p.new_dir = DIR_FROM_DXDY(xdiff, ydiff);
476                     if (p.new_dir == p.dir)
477                               return ("Already going in that direction");
478           }
479           return (NULL);
480 }
481 
482 static const char *
beacon(int c __unused)483 beacon(int c __unused)
484 {
485           dest_type = T_BEACON;
486           return (NULL);
487 }
488 
489 static const char *
ex_it(int c __unused)490 ex_it(int c __unused)
491 {
492           dest_type = T_EXIT;
493           return (NULL);
494 }
495 
496 static const char *
airport(int c __unused)497 airport(int c __unused)
498 {
499           dest_type = T_AIRPORT;
500           return (NULL);
501 }
502 
503 static const char *
climb(int c __unused)504 climb(int c __unused)
505 {
506           dir = D_UP;
507           return (NULL);
508 }
509 
510 static const char *
descend(int c __unused)511 descend(int c __unused)
512 {
513           dir = D_DOWN;
514           return (NULL);
515 }
516 
517 static const char *
setalt(int c)518 setalt(int c)
519 {
520           if ((p.altitude == c - '0') && (p.new_altitude == p.altitude))
521                     return ("Already at that altitude");
522           if (p.new_altitude == c - '0')
523                     return ("Already going to that altitude");
524           p.new_altitude = c - '0';
525           return (NULL);
526 }
527 
528 static const char *
setrelalt(int c)529 setrelalt(int c)
530 {
531           int new_altitude;
532 
533           if (c == 0)
534                     return ("altitude not changed");
535 
536           switch (dir) {
537           case D_UP:
538                     new_altitude = p.altitude + c - '0';
539                     break;
540           case D_DOWN:
541                     new_altitude = p.altitude - (c - '0');
542                     break;
543           default:
544                     return ("Unknown case in setrelalt!  Get help!");
545                     break;
546           }
547           if (new_altitude < 0)
548                     return ("Altitude would be too low");
549           else if (new_altitude > 9)
550                     return ("Altitude would be too high");
551           else if (new_altitude == p.new_altitude)
552                     return ("Already going to that altitude");
553 
554           p.new_altitude = new_altitude;
555           return (NULL);
556 }
557 
558 static const char *
benum(int c)559 benum(int c)
560 {
561           dest_no = c -= '0';
562 
563           switch (dest_type) {
564           case T_BEACON:
565                     if (c >= sp->num_beacons)
566                               return ("Unknown beacon");
567                     p.new_dir = DIR_FROM_DXDY(sp->beacon[c].x - p.xpos,
568                               sp->beacon[c].y - p.ypos);
569                     break;
570           case T_EXIT:
571                     if (c >= sp->num_exits)
572                               return ("Unknown exit");
573                     p.new_dir = DIR_FROM_DXDY(sp->exit[c].x - p.xpos,
574                               sp->exit[c].y - p.ypos);
575                     break;
576           case T_AIRPORT:
577                     if (c >= sp->num_airports)
578                               return ("Unknown airport");
579                     p.new_dir = DIR_FROM_DXDY(sp->airport[c].x - p.xpos,
580                               sp->airport[c].y - p.ypos);
581                     break;
582           default:
583                     return ("Unknown case in benum!  Get help!");
584                     break;
585           }
586           return (NULL);
587 }
588 
589 static const char *
to_dir(int c)590 to_dir(int c)
591 {
592           p.new_dir = dir_no(c);
593           return (NULL);
594 }
595 
596 static const char *
rel_dir(int c)597 rel_dir(int c)
598 {
599           int       angle;
600 
601           angle = dir_no(c);
602           switch (dir) {
603           case D_LEFT:
604                     p.new_dir = p.dir - angle;
605                     if (p.new_dir < 0)
606                               p.new_dir += MAXDIR;
607                     break;
608           case D_RIGHT:
609                     p.new_dir = p.dir + angle;
610                     if (p.new_dir >= MAXDIR)
611                               p.new_dir -= MAXDIR;
612                     break;
613           default:
614                     return ("Bizarre direction in rel_dir!  Get help!");
615                     break;
616           }
617           return (NULL);
618 }
619 
620 static const char *
mark(int c __unused)621 mark(int c __unused)
622 {
623           if (p.altitude == 0)
624                     return ("Cannot mark planes on the ground");
625           if (p.status == S_MARKED)
626                     return ("Already marked");
627           p.status = S_MARKED;
628           return (NULL);
629 }
630 
631 static const char *
unmark(int c __unused)632 unmark(int c __unused)
633 {
634           if (p.altitude == 0)
635                     return ("Cannot unmark planes on the ground");
636           if (p.status == S_UNMARKED)
637                     return ("Already unmarked");
638           p.status = S_UNMARKED;
639           return (NULL);
640 }
641 
642 static const char *
ignore(int c __unused)643 ignore(int c __unused)
644 {
645           if (p.altitude == 0)
646                     return ("Cannot ignore planes on the ground");
647           if (p.status == S_IGNORED)
648                     return ("Already ignored");
649           p.status = S_IGNORED;
650           return (NULL);
651 }
652 
653 int
dir_no(char ch)654 dir_no(char ch)
655 {
656           int       dirno;
657 
658           dirno = dir;
659           switch (ch) {
660           case 'w': dirno = 0;          break;
661           case 'e': dirno = 1;          break;
662           case 'd': dirno = 2;          break;
663           case 'c': dirno = 3;          break;
664           case 'x': dirno = 4;          break;
665           case 'z': dirno = 5;          break;
666           case 'a': dirno = 6;          break;
667           case 'q': dirno = 7;          break;
668           default:
669                     fprintf(stderr, "bad character in dir_no\n");
670                     break;
671           }
672           return (dirno);
673 }
674