xref: /original-bsd/games/phantasia/io.c (revision 79cf7955)
1 /*
2  * io.c - input/output routines for Phantasia
3  */
4 
5 #include "include.h"
6 
7 /************************************************************************
8 /
9 / FUNCTION NAME: getstring()
10 /
11 / FUNCTION: read a string from operator
12 /
13 / AUTHOR: E. A. Estes, 12/4/85
14 /
15 / ARGUMENTS:
16 /	char *cp - pointer to buffer area to fill
17 /	int mx - maximum number of characters to put in buffer
18 /
19 / RETURN VALUE: none
20 /
21 / MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
22 /	wclrtoeol()
23 /
24 / GLOBAL INPUTS: Echo, _iob[], Wizard, *stdscr
25 /
26 / GLOBAL OUTPUTS: _iob[]
27 /
28 / DESCRIPTION:
29 /	Read a string from the keyboard.
30 /	This routine is specially designed to:
31 /
32 /	    - strip non-printing characters (unless Wizard)
33 /	    - echo, if desired
34 /	    - redraw the screen if CH_REDRAW is entered
35 /	    - read in only 'mx - 1' characters or less characters
36 /	    - nul-terminate string, and throw away newline
37 /
38 /	'mx' is assumed to be at least 2.
39 /
40 /************************************************************************/
41 
42 getstring(cp, mx)
43 register char	*cp;
44 register int	mx;
45 {
46 register char	*inptr;		/* pointer into string for next string */
47 int	x, y;			/* original x, y coordinates on screen */
48 int	ch;			/* input */
49 
50     getyx(stdscr, y, x);	/* get coordinates on screen */
51     inptr = cp;
52     *inptr = '\0';		/* clear string to start */
53     --mx;			/* reserve room in string for nul terminator */
54 
55     do
56 	/* get characters and process */
57 	{
58 	if (Echo)
59 	    mvaddstr(y, x, cp);	/* print string on screen */
60 	clrtoeol();		/* clear any data after string */
61 	refresh();		/* update screen */
62 
63 	ch = getchar();		/* get character */
64 
65 	switch (ch)
66 	    {
67 	    case CH_ERASE:	/* back up one character */
68 		if (inptr > cp)
69 		    --inptr;
70 		break;
71 
72 	    case CH_KILL:	/* back up to original location */
73 		inptr = cp;
74 		break;
75 
76 	    case CH_NEWLINE:	/* terminate string */
77 		break;
78 
79 	    case CH_REDRAW:	/* redraw screen */
80 		clearok(stdscr, TRUE);
81 		continue;
82 
83 	    default:		/* put data in string */
84 		if (ch >= ' ' || Wizard)
85 		    /* printing char; put in string */
86 		    *inptr++ = ch;
87 	    }
88 
89 	*inptr = '\0';		/* terminate string */
90 	}
91     while (ch != CH_NEWLINE && inptr < cp + mx);
92 }
93 /**/
94 /************************************************************************
95 /
96 / FUNCTION NAME: more()
97 /
98 / FUNCTION: pause and prompt player
99 /
100 / AUTHOR: E. A. Estes, 12/4/85
101 /
102 / ARGUMENTS:
103 /	int where - line on screen on which to pause
104 /
105 / RETURN VALUE: none
106 /
107 / MODULES CALLED: wmove(), waddstr(), getanswer()
108 /
109 / GLOBAL INPUTS: *stdscr
110 /
111 / GLOBAL OUTPUTS: none
112 /
113 / DESCRIPTION:
114 /	Print a message, and wait for a space character.
115 /
116 /************************************************************************/
117 
118 more(where)
119 int	where;
120 {
121     mvaddstr(where, 0, "-- more --");
122     getanswer(" ", FALSE);
123 }
124 /**/
125 /************************************************************************
126 /
127 / FUNCTION NAME: infloat()
128 /
129 / FUNCTION: input a floating point number from operator
130 /
131 / AUTHOR: E. A. Estes, 12/4/85
132 /
133 / ARGUMENTS: none
134 /
135 / RETURN VALUE: floating point number from operator
136 /
137 / MODULES CALLED: sscanf(), getstring()
138 /
139 / GLOBAL INPUTS: Databuf[]
140 /
141 / GLOBAL OUTPUTS: none
142 /
143 / DESCRIPTION:
144 /	Read a string from player, and scan for a floating point
145 /	number.
146 /	If no valid number is found, return 0.0.
147 /
148 /************************************************************************/
149 
150 double
151 infloat()
152 {
153 double	result;		/* return value */
154 
155     getstring(Databuf, SZ_DATABUF);
156     if (sscanf(Databuf, "%lf", &result) < 1)
157 	/* no valid number entered */
158 	result = 0.0;
159 
160     return(result);
161 }
162 /**/
163 /************************************************************************
164 /
165 / FUNCTION NAME: inputoption()
166 /
167 / FUNCTION: input an option value from player
168 /
169 / AUTHOR: E. A. Estes, 12/4/85
170 /
171 / ARGUMENTS: none
172 /
173 / RETURN VALUE: none
174 /
175 / MODULES CALLED: floor(), drandom(), getanswer()
176 /
177 / GLOBAL INPUTS: Player
178 /
179 / GLOBAL OUTPUTS: Player
180 /
181 / DESCRIPTION:
182 /	Age increases with every move.
183 /	Refresh screen, and get a single character option from player.
184 /	Return a random value if player's ring has gone bad.
185 /
186 /************************************************************************/
187 
188 inputoption()
189 {
190     ++Player.p_age;		/* increase age */
191 
192     if (Player.p_ring.ring_type != R_SPOILED)
193 	/* ring ok */
194 	return(getanswer("T ", TRUE));
195     else
196 	/* bad ring */
197 	{
198 	getanswer(" ", TRUE);
199 	return((int) ROLL(0.0, 5.0) + '0');
200 	}
201 }
202 /**/
203 /************************************************************************
204 /
205 / FUNCTION NAME: interrupt()
206 /
207 / FUNCTION: handle interrupt from operator
208 /
209 / AUTHOR: E. A. Estes, 12/4/85
210 /
211 / ARGUMENTS: none
212 /
213 / RETURN VALUE: none
214 /
215 / MODULES CALLED: fork(), exit(), wait(), death(), alarm(), execl(), wmove(),
216 /	getgid(), signal(), getenv(), wclear(), setuid(), getuid(), setgid(),
217 /	crmode(), clearok(), waddstr(), cleanup(), wrefresh(), leavegame(),
218 /	getanswer()
219 /
220 / GLOBAL INPUTS: Player, *stdscr
221 /
222 / GLOBAL OUTPUTS: none
223 /
224 / DESCRIPTION:
225 /	Allow player to quit upon hitting the interrupt key.
226 /	If the player wants to quit while in battle, he/she automatically
227 /	dies.
228 /	If SHELL is defined, spawn a shell if the if the question is
229 /	answered with a '!'.
230 /	We are careful to save the state of the screen, and return it
231 /	to its original condition.
232 /
233 /************************************************************************/
234 
235 interrupt()
236 {
237 char	line[81];		/* a place to store data already on screen */
238 register int	loop;		/* counter */
239 int	x, y;			/* coordinates on screen */
240 int	ch;			/* input */
241 unsigned	savealarm;	/* to save alarm value */
242 #ifdef SHELL
243 register char	*shell;		/* pointer to shell to spawn */
244 int	childpid;		/* pid of spawned process */
245 #endif
246 
247 #ifdef SYS3
248     signal(SIGINT, SIG_IGN);
249 #endif
250 #ifdef SYS5
251     signal(SIGINT, SIG_IGN);
252 #endif
253 
254     savealarm = alarm(0);		/* turn off any alarms */
255 
256     getyx(stdscr, y, x);		/* save cursor location */
257 
258     for (loop = 0; loop < 80; ++loop)	/* save line on screen */
259 	{
260 	move(4, loop);
261 	line[loop] = inch();
262 	}
263     line[80] = '\0';			/* nul terminate */
264 
265     if (Player.p_status == S_INBATTLE || Player.p_status == S_MONSTER)
266 	/* in midst of fighting */
267 	{
268 	mvaddstr(4, 0, "Quitting now will automatically kill your character.  Still want to ? ");
269 #ifdef SHELL
270 	ch = getanswer("NY!", FALSE);
271 #else
272 	ch = getanswer("NY", FALSE);
273 #endif
274 	if (ch == 'Y')
275 	    death("Bailing out");
276 	    /*NOTREACHED*/
277 	}
278     else
279 	{
280 #ifdef SHELL
281 	mvaddstr(4, 0, "Do you really want to quit [! = Shell] ? ");
282 	ch = getanswer("NY!", FALSE);
283 #else
284 	mvaddstr(4, 0, "Do you really want to quit ? ");
285 	ch = getanswer("NY", FALSE);
286 #endif
287 	if (ch == 'Y')
288 	    leavegame();
289 	    /*NOTREACHED*/
290 	}
291 
292 #ifdef SHELL
293     if (ch == '!')
294 	/* shell escape */
295 	{
296 	if ((shell = getenv("SHELL")) == NULL)
297 	    /* use default */
298 	    shell = SHELL;
299 
300 	if ((childpid = fork()) == 0)
301 	    /* in child */
302 	    {
303 	    clear();
304 	    refresh();
305 	    cleanup(FALSE);		/* out of curses, close files */
306 
307 	    setuid(getuid());		/* make sure we are running with real uid */
308 	    setgid(getgid());		/* make sure we are running with real gid */
309 	    execl(shell, shell, "-i", 0);
310 	    execl(SHELL, SHELL, "-i", 0);	/* last resort */
311 
312 	    exit(0);
313 	    /*NOTREACHED*/
314 	    }
315 	else
316 	    /* in parent */
317 	    {
318 	    while (wait((int *) NULL) != childpid);	/* wait until done */
319 	    crmode();			/* restore keyboard */
320 	    clearok(stdscr, TRUE);	/* force redraw of screen */
321 	    }
322 	}
323 #endif
324 
325     mvaddstr(4, 0, line); 		/* restore data on screen */
326     move(y, x);				/* restore cursor */
327     refresh();
328 
329 #ifdef SYS3
330     signal(SIGINT, interrupt);
331 #endif
332 #ifdef SYS5
333     signal(SIGINT, interrupt);
334 #endif
335 
336     alarm(savealarm);			/* restore alarm */
337 }
338 /**/
339 /************************************************************************
340 /
341 / FUNCTION NAME: getanswer()
342 /
343 / FUNCTION: get an answer from operator
344 /
345 / AUTHOR: E. A. Estes, 12/4/85
346 /
347 / ARGUMENTS:
348 /	char *choices - string of (upper case) valid choices
349 /	bool def - set if default answer
350 /
351 / RETURN VALUE: none
352 /
353 / MODULES CALLED: alarm(), wmove(), waddch(), signal(), setjmp(), strchr(),
354 /	_filbuf(), clearok(), toupper(), wrefresh(), mvprintw(), wclrtoeol()
355 /
356 / GLOBAL INPUTS: catchalarm(), Echo, _iob[], _ctype[], *stdscr, Timeout,
357 /	Timeoenv[]
358 /
359 / GLOBAL OUTPUTS: _iob[]
360 /
361 / DESCRIPTION:
362 /	Get a single character answer from operator.
363 /	Timeout waiting for response.  If we timeout, or the
364 /	answer in not in the list of valid choices, print choices,
365 /	and wait again, otherwise return the first character in ths
366 /	list of choices.
367 /	Give up after 3 tries.
368 /
369 /************************************************************************/
370 
371 getanswer(choices, def)
372 char	*choices;
373 bool	def;
374 {
375 int	ch;			/* input */
376 int	loop;			/* counter */
377 int	oldx, oldy;		/* original coordinates on screen */
378 
379     getyx(stdscr, oldy, oldx);
380     alarm(0);				/* make sure alarm is off */
381 
382     for (loop = 3; loop; --loop)
383 	/* try for 3 times */
384 	{
385 	if (setjmp(Timeoenv) != 0)
386 	    /* timed out waiting for response */
387 	    {
388 	    if (def || loop <= 1)
389 		/* return default answer */
390 		break;
391 	    else
392 		/* prompt, and try again */
393 		goto YELL;
394 	    }
395 	else
396 	    /* wait for response */
397 	    {
398 	    clrtoeol();
399 	    refresh();
400 #ifdef BSD41
401 	    sigset(SIGALRM, catchalarm);
402 #else
403 	    signal(SIGALRM, catchalarm);
404 #endif
405 	    /* set timeout */
406 	    if (Timeout)
407 		alarm(7);		/* short */
408 	    else
409 		alarm(600);		/* long */
410 
411 	    ch = getchar();
412 
413 	    alarm(0);			/* turn off timeout */
414 
415 	    if (ch < 0)
416 		/* caught some signal */
417 		{
418 		++loop;
419 		continue;
420 		}
421 	    else if (ch == CH_REDRAW)
422 		/* redraw screen */
423 		{
424 		clearok(stdscr, TRUE);	/* force clear screen */
425 		++loop;			/* don't count this input */
426 		continue;
427 		}
428 	    else if (Echo)
429 		{
430 		addch(ch);		/* echo character */
431 		refresh();
432 		}
433 
434 	    if (islower(ch))
435 		/* convert to upper case */
436 		ch = toupper(ch);
437 
438 	    if (def || strchr(choices, ch) != NULL)
439 		/* valid choice */
440 		return(ch);
441 	    else if (!def && loop > 1)
442 		/* bad choice; prompt, and try again */
443 		{
444 YELL:		mvprintw(oldy + 1, 0, "Please choose one of : [%s]\n", choices);
445 		move(oldy, oldx);
446 		clrtoeol();
447 		continue;
448 		}
449 	    else
450 		/* return default answer */
451 		break;
452 	    }
453 	}
454 
455     return(*choices);
456 }
457 /**/
458 /************************************************************************
459 /
460 / FUNCTION NAME: catchalarm()
461 /
462 / FUNCTION: catch timer when waiting for input
463 /
464 / AUTHOR: E. A. Estes, 12/4/85
465 /
466 / ARGUMENTS: none
467 /
468 / RETURN VALUE: none
469 /
470 / MODULES CALLED: longjmp()
471 /
472 / GLOBAL INPUTS: Timeoenv[]
473 /
474 / GLOBAL OUTPUTS: none
475 /
476 / DESCRIPTION:
477 /	Come here when the alarm expires while waiting for input.
478 /	Simply longjmp() into getanswer().
479 /
480 /************************************************************************/
481 
482 catchalarm()
483 {
484     longjmp(Timeoenv, 1);
485 }
486