Commit | Line | Data |
---|---|---|
d9d6b457 KB |
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 | /*\f*/ | |
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 | /*\f*/ | |
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); | |
4266ed4a | 156 | if (sscanf(Databuf, "%lf", &result) < 1) |
d9d6b457 KB |
157 | /* no valid number entered */ |
158 | result = 0.0; | |
159 | ||
160 | return(result); | |
161 | } | |
162 | /*\f*/ | |
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 | /*\f*/ | |
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 | /*\f*/ | |
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 | /*\f*/ | |
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 | } |