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