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 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 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 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 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 309 noise(void) 310 { 311 putchar('\07'); 312 fflush(stdout); 313 } 314 315 static int 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 483 beacon(int c __unused) 484 { 485 dest_type = T_BEACON; 486 return (NULL); 487 } 488 489 static const char * 490 ex_it(int c __unused) 491 { 492 dest_type = T_EXIT; 493 return (NULL); 494 } 495 496 static const char * 497 airport(int c __unused) 498 { 499 dest_type = T_AIRPORT; 500 return (NULL); 501 } 502 503 static const char * 504 climb(int c __unused) 505 { 506 dir = D_UP; 507 return (NULL); 508 } 509 510 static const char * 511 descend(int c __unused) 512 { 513 dir = D_DOWN; 514 return (NULL); 515 } 516 517 static const char * 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 * 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 * 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 * 590 to_dir(int c) 591 { 592 p.new_dir = dir_no(c); 593 return (NULL); 594 } 595 596 static const char * 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 * 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 * 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 * 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 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