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 } 675