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