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