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