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