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