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