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 * @(#)input.c 8.1 (Berkeley) 5/31/93 37 * $FreeBSD: src/games/atc/input.c,v 1.7 2000/02/27 23:02:47 mph Exp $ 38 * $DragonFly: src/games/atc/input.c,v 1.4 2006/09/09 02:21:49 pavalos Exp $ 39 */ 40 41 /* 42 * Copyright (c) 1987 by Ed James, UC Berkeley. All rights reserved. 43 * 44 * Copy permission is hereby granted provided that this notice is 45 * retained on all partial or complete copies. 46 * 47 * For more info on this and all of my stuff, mail edjames@berkeley.edu. 48 */ 49 50 #include <sys/wait.h> 51 52 #include <stdlib.h> 53 #include <string.h> 54 #include "include.h" 55 #include "pathnames.h" 56 57 #define MAXRULES 6 58 #define MAXDEPTH 15 59 60 #define RETTOKEN '\r' 61 #define REDRAWTOKEN '\014' /* CTRL(L) */ 62 #define SHELLTOKEN '!' 63 #define HELPTOKEN '?' 64 #define ALPHATOKEN 256 65 #define NUMTOKEN 257 66 67 typedef struct { 68 int token; 69 int to_state; 70 const char *str; 71 const char *(*func)(int); 72 } RULE; 73 74 typedef struct { 75 int num_rules; 76 RULE *rule; 77 } STATE; 78 79 typedef struct { 80 char str[20]; 81 int state; 82 int rule; 83 int ch; 84 int pos; 85 } STACK; 86 87 #define T_RULE stack[level].rule 88 #define T_STATE stack[level].state 89 #define T_STR stack[level].str 90 #define T_POS stack[level].pos 91 #define T_CH stack[level].ch 92 93 #define NUMELS(a) (sizeof (a) / sizeof (*(a))) 94 95 #define NUMSTATES NUMELS(st) 96 97 static int pop(void); 98 static void rezero(void); 99 static void push(int, int); 100 static void noise(void); 101 static int gettoken(void); 102 static const char *setplane(int); 103 static const char *turn(int); 104 static const char *circle(int); 105 static const char *left(int); 106 static const char *right(int); 107 static const char *Left(int); 108 static const char *Right(int); 109 static const char *delayb(int); 110 static const char *beacon(int); 111 static const char *ex_it(int); 112 static const char *airport(int); 113 static const char *climb(int); 114 static const char *descend(int); 115 static const char *setalt(int); 116 static const char *setrelalt(int); 117 static const char *benum(int); 118 static const char *to_dir(int); 119 static const char *rel_dir(int); 120 static const char *mark(int); 121 static const char *unmark(int); 122 static const char *ignore(int); 123 124 RULE state0[] = { { ALPHATOKEN, 1, "%c:", setplane}, 125 { RETTOKEN, -1, "", NULL }, 126 { HELPTOKEN, 12, " [a-z]<ret>", NULL }}, 127 state1[] = { { 't', 2, " turn", turn }, 128 { 'a', 3, " altitude:", NULL }, 129 { 'c', 4, " circle", circle }, 130 { 'm', 7, " mark", mark }, 131 { 'u', 7, " unmark", unmark }, 132 { 'i', 7, " ignore", ignore }, 133 { HELPTOKEN, 12, " tacmui", NULL }}, 134 state2[] = { { 'l', 6, " left", left }, 135 { 'r', 6, " right", right }, 136 { 'L', 4, " left 90", Left }, 137 { 'R', 4, " right 90", Right }, 138 { 't', 11, " towards", NULL }, 139 { 'w', 4, " to 0", to_dir }, 140 { 'e', 4, " to 45", to_dir }, 141 { 'd', 4, " to 90", to_dir }, 142 { 'c', 4, " to 135", to_dir }, 143 { 'x', 4, " to 180", to_dir }, 144 { 'z', 4, " to 225", to_dir }, 145 { 'a', 4, " to 270", to_dir }, 146 { 'q', 4, " to 315", to_dir }, 147 { HELPTOKEN, 12, " lrLRt<dir>", NULL }}, 148 state3[] = { { '+', 10, " climb", climb }, 149 { 'c', 10, " climb", climb }, 150 { '-', 10, " descend", descend }, 151 { 'd', 10, " descend", descend }, 152 { NUMTOKEN, 7, " %c000 feet", setalt }, 153 { HELPTOKEN, 12, " +-cd[0-9]", NULL }}, 154 state4[] = { { '@', 9, " at", NULL }, 155 { 'a', 9, " at", NULL }, 156 { RETTOKEN, -1, "", NULL }, 157 { HELPTOKEN, 12, " @a<ret>", NULL }}, 158 state5[] = { { NUMTOKEN, 7, "%c", delayb }, 159 { HELPTOKEN, 12, " [0-9]", NULL }}, 160 state6[] = { { '@', 9, " at", NULL }, 161 { 'a', 9, " at", NULL }, 162 { 'w', 4, " 0", rel_dir }, 163 { 'e', 4, " 45", rel_dir }, 164 { 'd', 4, " 90", rel_dir }, 165 { 'c', 4, " 135", rel_dir }, 166 { 'x', 4, " 180", rel_dir }, 167 { 'z', 4, " 225", rel_dir }, 168 { 'a', 4, " 270", rel_dir }, 169 { 'q', 4, " 315", rel_dir }, 170 { RETTOKEN, -1, "", NULL }, 171 { HELPTOKEN, 12, " @a<dir><ret>",NULL }}, 172 state7[] = { { RETTOKEN, -1, "", NULL }, 173 { HELPTOKEN, 12, " <ret>", NULL }}, 174 state8[] = { { NUMTOKEN, 4, "%c", benum }, 175 { HELPTOKEN, 12, " [0-9]", NULL }}, 176 state9[] = { { 'b', 5, " beacon #", NULL }, 177 { '*', 5, " beacon #", NULL }, 178 { HELPTOKEN, 12, " b*", NULL }}, 179 state10[] = { { NUMTOKEN, 7, " %c000 ft", setrelalt}, 180 { HELPTOKEN, 12, " [0-9]", NULL }}, 181 state11[] = { { 'b', 8, " beacon #", beacon }, 182 { '*', 8, " beacon #", beacon }, 183 { 'e', 8, " exit #", ex_it }, 184 { 'a', 8, " airport #", airport }, 185 { HELPTOKEN, 12, " b*ea", NULL }}, 186 state12[] = { { -1, -1, "", NULL }}; 187 188 #define DEF_STATE(s) { NUMELS(s), (s) } 189 190 STATE st[] = { 191 DEF_STATE(state0), DEF_STATE(state1), DEF_STATE(state2), 192 DEF_STATE(state3), DEF_STATE(state4), DEF_STATE(state5), 193 DEF_STATE(state6), DEF_STATE(state7), DEF_STATE(state8), 194 DEF_STATE(state9), DEF_STATE(state10), DEF_STATE(state11), 195 DEF_STATE(state12) 196 }; 197 198 PLANE p; 199 STACK stack[MAXDEPTH]; 200 int level; 201 int tval; 202 int dest_type, dest_no, dir; 203 204 205 static int 206 pop(void) 207 { 208 if (level == 0) 209 return (-1); 210 level--; 211 212 ioclrtoeol(T_POS); 213 214 strcpy(T_STR, ""); 215 T_RULE = -1; 216 T_CH = -1; 217 return (0); 218 } 219 220 static void 221 rezero(void) 222 { 223 iomove(0); 224 225 level = 0; 226 T_STATE = 0; 227 T_RULE = -1; 228 T_CH = -1; 229 T_POS = 0; 230 strcpy(T_STR, ""); 231 } 232 233 static void 234 push(int ruleno, int ch) 235 { 236 int newstate, newpos; 237 238 (void)sprintf(T_STR, st[T_STATE].rule[ruleno].str, tval); 239 T_RULE = ruleno; 240 T_CH = ch; 241 newstate = st[T_STATE].rule[ruleno].to_state; 242 newpos = T_POS + strlen(T_STR); 243 244 ioaddstr(T_POS, T_STR); 245 246 if (level == 0) 247 ioclrtobot(); 248 level++; 249 T_STATE = newstate; 250 T_POS = newpos; 251 T_RULE = -1; 252 strcpy(T_STR, ""); 253 } 254 255 int 256 getcommand(void) 257 { 258 int c, i, done; 259 const char *s, *(*func)(int); 260 PLANE *pp; 261 262 rezero(); 263 264 do { 265 c = gettoken(); 266 if (c == tty_new.c_cc[VERASE]) { 267 if (pop() < 0) 268 noise(); 269 } else if (c == tty_new.c_cc[VKILL]) { 270 while (pop() >= 0) 271 ; 272 } else { 273 done = 0; 274 for (i = 0; i < st[T_STATE].num_rules; i++) { 275 if (st[T_STATE].rule[i].token == c || 276 st[T_STATE].rule[i].token == tval) { 277 push(i, (c >= ALPHATOKEN) ? tval : c); 278 done = 1; 279 break; 280 } 281 } 282 if (!done) 283 noise(); 284 } 285 } while (T_STATE != -1); 286 287 if (level == 1) 288 return (1); /* forced update */ 289 290 dest_type = T_NODEST; 291 292 for (i = 0; i < level; i++) { 293 func = st[stack[i].state].rule[stack[i].rule].func; 294 if (func != NULL) 295 if ((s = (*func)(stack[i].ch)) != NULL) { 296 ioerror(stack[i].pos, strlen(stack[i].str), s); 297 return (-1); 298 } 299 } 300 301 pp = findplane(p.plane_no); 302 if (pp->new_altitude != p.new_altitude) 303 pp->new_altitude = p.new_altitude; 304 else if (pp->status != p.status) 305 pp->status = p.status; 306 else { 307 pp->new_dir = p.new_dir; 308 pp->delayd = p.delayd; 309 pp->delayd_no = p.delayd_no; 310 } 311 return (0); 312 } 313 314 static void 315 noise(void) 316 { 317 putchar('\07'); 318 fflush(stdout); 319 } 320 321 static int 322 gettoken(void) 323 { 324 while ((tval = getAChar()) == REDRAWTOKEN || tval == SHELLTOKEN) 325 { 326 if (tval == SHELLTOKEN) 327 { 328 struct itimerval itv; 329 itv.it_value.tv_sec = 0; 330 itv.it_value.tv_usec = 0; 331 setitimer(ITIMER_REAL, &itv, NULL); 332 if (fork() == 0) /* child */ 333 { 334 char *shell, *base; 335 336 /* revoke */ 337 setgid(getgid()); 338 done_screen(); 339 340 /* run user's favorite shell */ 341 if ((shell = getenv("SHELL")) != NULL) 342 { 343 base = strrchr(shell, '/'); 344 if (base == NULL) 345 base = shell; 346 else 347 base++; 348 execl(shell, base, NULL); 349 } 350 else 351 execl(_PATH_BSHELL, "sh", NULL); 352 353 exit(0); /* oops */ 354 } 355 356 wait(0); 357 tcsetattr(fileno(stdin), TCSANOW, &tty_new); 358 itv.it_value.tv_sec = 0; 359 itv.it_value.tv_usec = 1; 360 itv.it_interval.tv_sec = sp->update_secs; 361 itv.it_interval.tv_usec = 0; 362 setitimer(ITIMER_REAL, &itv, NULL); 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 static const char * 376 setplane(int 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 static const char * 389 turn(__unused int c) 390 { 391 if (p.altitude == 0) 392 return ("Planes at airports may not change direction"); 393 return (NULL); 394 } 395 396 static const char * 397 circle(__unused int 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 static const char * 406 left(__unused int 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 static const char * 416 right(__unused int 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 static const char * 426 Left(__unused int 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 static const char * 435 Right(__unused int 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 static const char * 444 delayb(int 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 static const char * 489 beacon(__unused int c) 490 { 491 dest_type = T_BEACON; 492 return (NULL); 493 } 494 495 static const char * 496 ex_it(__unused int c) 497 { 498 dest_type = T_EXIT; 499 return (NULL); 500 } 501 502 static const char * 503 airport(__unused int c) 504 { 505 dest_type = T_AIRPORT; 506 return (NULL); 507 } 508 509 static const char * 510 climb(__unused int c) 511 { 512 dir = D_UP; 513 return (NULL); 514 } 515 516 static const char * 517 descend(__unused int c) 518 { 519 dir = D_DOWN; 520 return (NULL); 521 } 522 523 static const char * 524 setalt(int 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 static const char * 533 setrelalt(int 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 static const char * 557 benum(int 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 static const char * 588 to_dir(int c) 589 { 590 p.new_dir = dir_no(c); 591 return (NULL); 592 } 593 594 static const char * 595 rel_dir(int 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 static const char * 619 mark(__unused int 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 static const char * 630 unmark(__unused int 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 static const char * 641 ignore(__unused int 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 int 652 dir_no(char ch) 653 { 654 int dirno = dir; 655 656 switch (ch) { 657 case 'w': dirno = 0; break; 658 case 'e': dirno = 1; break; 659 case 'd': dirno = 2; break; 660 case 'c': dirno = 3; break; 661 case 'x': dirno = 4; break; 662 case 'z': dirno = 5; break; 663 case 'a': dirno = 6; break; 664 case 'q': dirno = 7; break; 665 default: 666 fprintf(stderr, "bad character in dir_no\n"); 667 break; 668 } 669 return (dirno); 670 } 671