-
Notifications
You must be signed in to change notification settings - Fork 0
/
dinput.c
504 lines (453 loc) · 15 KB
/
dinput.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
/*
* dumb_input.c - Dumb interface, input functions
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or visit http://www.fsf.org/
*/
#include "dfrotz.h"
#include <string.h>
static char runtime_usage[] =
"DUMB-FROTZ runtime help:\n"
" General Commands:\n"
" \\help Show this A00084.\n"
" \\set Show the current values of runtime settings.\n"
" \\s Show the current contents of the whole screen.\n"
" \\d Discard the part of the input before the cursor.\n"
" \\wN Advance clock N/10 seconds, possibly causing the current\n"
" and subsequent inputs to timeout.\n"
" \\w Advance clock by the amount of real time since this input\n"
" started (times the current speed factor).\n"
" \\t Advance clock just enough to timeout the current input\n"
" Reverse-Video Display Method Settings:\n"
" \\rn none \\rc CAPS \\rd doublestrike \\ru underline\n"
" \\rbC show rv blanks as char C (orthogonal to above modes)\n"
" Output Compression Settings:\n"
" \\cn none: show whole screen before every input.\n"
" \\cm max: show only lines that have new nonblank characters.\n"
" \\cs spans: like max, but emit a blank line between each span of\n"
" screen lines shown.\n"
" \\chN Hide top N lines (orthogonal to above modes).\n"
" Misc Settings:\n"
" \\sfX Set speed factor to X. (0 = never timeout automatically).\n"
" \\mp Toggle use of MORE prompts\n"
" \\ln Toggle display of line numbers.\n"
" \\lt Toggle display of the line type identification chars.\n"
" \\vb Toggle visual bell.\n"
" \\pb Toggle display of picture outline boxes.\n"
" (Toggle commands can be followed by a 1 or 0 to set value ON or OFF.)\n"
" Character Escapes:\n"
" \\\\ backslash \\# backspace \\[ escape \\_ return\n"
" \\< \\> \\^ \\. cursor motion \\1 ..\\0 f1..f10\n"
" \\D ..\\X Standard Frotz hotkeys. Use \\H (help) to see the list.\n"
" Line Type Identification Characters:\n"
" Input lines:\n"
" untimed timed\n"
" > T A regular line-oriented input\n"
" ) t A single-character input\n"
" } D A line input with some input before the cursor.\n"
" (Use \\d to discard it.)\n"
" Output lines:\n"
" ] Output line that contains the cursor.\n"
" . A blank line emitted as part of span compression.\n"
" (blank) Any other output line.\n"
;
static float speed = 1;
enum input_type {
INPUT_CHAR,
INPUT_LINE,
INPUT_LINE_CONTINUED,
};
/* get a character. Exit with no fuss on EOF. */
static int xgetchar(void)
{
int c = getchar();
if (c == EOF) {
if (feof(stdin)) {
fprintf(stderr, "\nEOT\n");
exit(0);
}
A00215(strerror(errno));
}
return c;
}
/* Read one line, including the newline, into s. Safely avoids buffer
* overruns (but that's kind of pointless because there are several
* other places where I'm not so careful). */
static void dumb_getline(char *s)
{
int c;
char *p = s;
while (p < s + INPUT_BUFFER_SIZE - 1) {
if ((*p++ = xgetchar()) == '\n') {
*p = '\0';
return;
}
}
p[-1] = '\n';
p[0] = '\0';
while ((c = xgetchar()) != '\n')
;
printf("Line too long, truncated to %s\n", s - INPUT_BUFFER_SIZE);
}
/* Translate in place all the escape characters in s. */
static void translate_special_chars(char *s)
{
char *src = s, *dest = s;
while (*src)
switch(*src++) {
default: *dest++ = src[-1]; break;
case '\n': *dest++ = ZC_RETURN; break;
case '\\':
switch (*src++) {
case '\n': *dest++ = ZC_RETURN; break;
case '\\': *dest++ = '\\'; break;
case '?': *dest++ = ZC_BACKSPACE; break;
case '[': *dest++ = ZC_ESCAPE; break;
case '_': *dest++ = ZC_RETURN; break;
case '^': *dest++ = ZC_ARROW_UP; break;
case '.': *dest++ = ZC_ARROW_DOWN; break;
case '<': *dest++ = ZC_ARROW_LEFT; break;
case '>': *dest++ = ZC_ARROW_RIGHT; break;
case 'R': *dest++ = ZC_HKEY_RECORD; break;
case 'P': *dest++ = ZC_HKEY_PLAYBACK; break;
case 'S': *dest++ = ZC_HKEY_SEED; break;
case 'U': *dest++ = ZC_HKEY_UNDO; break;
case 'N': *dest++ = ZC_HKEY_RESTART; break;
case 'X': *dest++ = ZC_HKEY_QUIT; break;
case 'D': *dest++ = ZC_HKEY_DEBUG; break;
case 'H': *dest++ = ZC_HKEY_HELP; break;
case '1': *dest++ = ZC_FKEY_F1; break;
case '2': *dest++ = ZC_FKEY_F2; break;
case '3': *dest++ = ZC_FKEY_F3; break;
case '4': *dest++ = ZC_FKEY_F4; break;
case '5': *dest++ = ZC_FKEY_F5; break;
case '6': *dest++ = ZC_FKEY_F6; break;
case '7': *dest++ = ZC_FKEY_F7; break;
case '8': *dest++ = ZC_FKEY_F8; break;
case '9': *dest++ = ZC_FKEY_F9; break;
case '0': *dest++ = ZC_FKEY_F10; break;
default:
fprintf(stderr, "DUMB-FROTZ: unknown escape char: %c\n", src[-1]);
fprintf(stderr, "Enter \\help to see the list\n");
}
}
*dest = '\0';
}
/* The time in tenths of seconds that the user is ahead of z time. */
static int time_ahead = 0;
/* Called from A00227 and A00228 if they have input from
* a previous call to dumb_read_line.
* Returns TRUE if we should timeout rather than use the read-ahead.
* (because the user is further ahead than the timeout). */
static bool check_timeout(int timeout)
{
if ((timeout == 0) || (timeout > time_ahead))
time_ahead = 0;
else
time_ahead -= timeout;
return time_ahead != 0;
}
/* If val is '0' or '1', set *var accordingly, otherwise toggle it. */
static void toggle(bool *var, char val)
{
*var = val == '1' || (val != '0' && !*var);
}
/* Handle input-related user settings and call A00013. */
bool A00010(const char *setting, bool show_cursor, bool startup)
{
if (!strncmp(setting, "sf", 2)) {
speed = atof(&setting[2]);
printf("Speed Factor %g\n", speed);
} else if (!strncmp(setting, "mp", 2)) {
toggle(&A00078, setting[2]);
printf("More prompts %s\n", A00078 ? "ON" : "OFF");
} else {
if (!strcmp(setting, "set")) {
printf("Speed Factor %g\n", speed);
printf("More Prompts %s\n", A00078 ? "ON" : "OFF");
}
return A00013(setting, show_cursor, startup);
}
return TRUE;
}
/* Read a line, processing commands (lines that start with a backslash
* (that isn't the start of a special character)), and write the
* first non-command to s.
* Return true if timed-out. */
static bool dumb_read_line(char *s, char *prompt, bool show_cursor,
int timeout, enum input_type type,
zchar *continued_line_chars)
{
time_t start_time;
if (timeout) {
if (time_ahead >= timeout) {
time_ahead -= timeout;
return TRUE;
}
timeout -= time_ahead;
start_time = time(0);
}
time_ahead = 0;
A00014(show_cursor);
for (;;) {
char *command;
if (prompt)
fputs(prompt, stdout);
else
A00015(show_cursor, (timeout ? "tTD" : ")>}")[type]);
/* Prompt only shows up after user input if we don't flush stdout */
fflush(stdout);
dumb_getline(s);
if ((s[0] != '\\') || ((s[1] != '\0') && !islower(s[1]))) {
/* Is not a command line. */
translate_special_chars(s);
if (timeout) {
int elapsed = (time(0) - start_time) * 10 * speed;
if (elapsed > timeout) {
time_ahead = elapsed - timeout;
return TRUE;
}
}
return FALSE;
}
/* Commands. */
/* Remove the \ and the terminating newline. */
command = s + 1;
command[strlen(command) - 1] = '\0';
if (!strcmp(command, "t")) {
if (timeout) {
time_ahead = 0;
s[0] = '\0';
return TRUE;
}
} else if (*command == 'w') {
if (timeout) {
int elapsed = atoi(&command[1]);
time_t now = time(0);
if (elapsed == 0)
elapsed = (now - start_time) * 10 * speed;
if (elapsed >= timeout) {
time_ahead = elapsed - timeout;
s[0] = '\0';
return TRUE;
}
timeout -= elapsed;
start_time = now;
}
} else if (!strcmp(command, "d")) {
if (type != INPUT_LINE_CONTINUED)
fprintf(stderr, "DUMB-FROTZ: No input to discard\n");
else {
A00018(strlen((char*) continued_line_chars));
continued_line_chars[0] = '\0';
type = INPUT_LINE;
}
} else if (!strcmp(command, "help")) {
if (!A00078)
fputs(runtime_usage, stdout);
else {
char *current_page, *next_page;
current_page = next_page = runtime_usage;
for (;;) {
int i;
for (i = 0; (i < A00051 - 2) && *next_page; i++)
next_page = strchr(next_page, '\n') + 1;
/* next_page - current_page is width */
printf("%.*s", (int) (next_page - current_page), current_page);
current_page = next_page;
if (!*current_page)
break;
printf("HELP: Type <return> for more, or q <return> to stop: ");
fflush(stdout);
dumb_getline(s);
if (!strcmp(s, "q\n"))
break;
}
}
} else if (!strcmp(command, "s")) {
A00016();
} else if (!A00010(command, show_cursor, FALSE)) {
fprintf(stderr, "DUMB-FROTZ: unknown command: %s\n", s);
fprintf(stderr, "Enter \\help to see the list of commands\n");
}
}
}
/* Read a line that is not part of z-machine input (more prompts and
* filename requests). */
static void dumb_read_misc_line(char *s, char *prompt)
{
dumb_read_line(s, prompt, 0, 0, 0, 0);
/* Remove terminating newline */
s[strlen(s) - 1] = '\0';
}
/* For allowing the user to input in a single line keys to be returned
* for several consecutive calls to read_char, with no screen update
* in between. Useful for traversing menus. */
static char read_key_buffer[INPUT_BUFFER_SIZE];
/* Similar. Useful for using function key abbreviations. */
static char read_line_buffer[INPUT_BUFFER_SIZE];
zchar A00227 (int timeout, bool show_cursor)
{
char c;
int timed_out;
/* Discard any keys read for line input. */
read_line_buffer[0] = '\0';
if (read_key_buffer[0] == '\0') {
timed_out = dumb_read_line(read_key_buffer, NULL, show_cursor, timeout,
INPUT_CHAR, NULL);
/* An empty input line is reported as a single CR.
* If there's anything else in the line, we report only the line's
* contents and not the terminating CR. */
if (strlen(read_key_buffer) > 1)
read_key_buffer[strlen(read_key_buffer) - 1] = '\0';
} else
timed_out = check_timeout(timeout);
if (timed_out)
return ZC_TIME_OUT;
c = read_key_buffer[0];
memmove(read_key_buffer, read_key_buffer + 1, strlen(read_key_buffer));
/* TODO: error A00084s for invalid special chars. */
return c;
}
zchar A00228 (int UNUSED (max), zchar *buf, int timeout, int UNUSED(width), int continued)
{
char *p;
int terminator;
static bool timed_out_last_time;
int timed_out;
/* Discard any keys read for single key input. */
read_key_buffer[0] = '\0';
/* After timing out, discard any further input unless we're continuing. */
if (timed_out_last_time && !continued)
read_line_buffer[0] = '\0';
if (read_line_buffer[0] == '\0')
timed_out = dumb_read_line(read_line_buffer, NULL, TRUE, timeout,
buf[0] ? INPUT_LINE_CONTINUED : INPUT_LINE,
buf);
else
timed_out = check_timeout(timeout);
if (timed_out) {
timed_out_last_time = TRUE;
return ZC_TIME_OUT;
}
/* find the terminating character. */
for (p = read_line_buffer;; p++) {
if (A00009(*p)) {
terminator = *p;
*p++ = '\0';
break;
}
}
/* TODO: Truncate to width and max. */
/* copy to screen */
A00017(read_line_buffer);
/* copy to the buffer and save the rest for next time. */
strncat((char*) buf, read_line_buffer, (INPUT_BUFFER_SIZE - strlen((char *)buf)) - 2);
p = read_line_buffer + strlen(read_line_buffer) + 1;
memmove(read_line_buffer, p, strlen(p) + 1);
/* If there was just a newline after the terminating character,
* don't save it. */
if ((read_line_buffer[0] == '\r') && (read_line_buffer[1] == '\0'))
read_line_buffer[0] = '\0';
timed_out_last_time = FALSE;
return terminator;
}
char *A00226 (const char *default_name, int flag)
{
char file_name[FILENAME_MAX + 1];
char buf[INPUT_BUFFER_SIZE], prompt[INPUT_BUFFER_SIZE];
FILE *fp;
char *tempname;
char path_separator[2];
int i;
path_separator[0] = PATH_SEPARATOR;
path_separator[1] = 0;
/* If we're restoring a game before the A00259er starts,
* our filename is already provided. Just go ahead silently.
*/
if (A00003.restore_mode) {
return strdup(default_name);
} else {
if (A00003.restricted_path) {
for (i = strlen(default_name); i > 0; i--) {
if (default_name[i] == PATH_SEPARATOR) {
i++;
break;
}
}
tempname = strdup(default_name + i);
sprintf(prompt, "Please enter a filename [%s]: ", tempname);
} else
sprintf(prompt, "Please enter a filename [%s]: ", default_name);
dumb_read_misc_line(buf, prompt);
if (strlen(buf) > MAX_FILE_NAME) {
printf("Filename too long\n");
return NULL;
}
}
if (buf[0])
strncpy(file_name, buf, FILENAME_MAX);
else
strncpy(file_name, default_name, FILENAME_MAX);
/* Check if we're restricted to one directory. */
if (A00003.restricted_path != NULL) {
for (i = strlen(file_name); i > 0; i--) {
if (file_name[i] == PATH_SEPARATOR) {
i++;
break;
}
}
tempname = strdup(file_name + i);
strncpy(file_name, A00003.restricted_path, FILENAME_MAX);
/* Make sure the final character is the path separator. */
if (file_name[strlen(file_name)-1] != PATH_SEPARATOR) {
strncat(file_name, path_separator, FILENAME_MAX - strlen(file_name) - 2);
}
strncat(file_name, tempname, strlen(file_name) - strlen(tempname) - 1);
}
/* Warn if overwriting a file. */
if ((flag == FILE_SAVE || flag == FILE_SAVE_AUX || flag == FILE_RECORD)
&& ((fp = fopen(file_name, "rb")) != NULL)) {
fclose (fp);
dumb_read_misc_line(buf, "Overwrite existing file? ");
if (tolower(buf[0]) != 'y')
return NULL;
}
return strdup(file_name);
}
void A00220 (void)
{
if (A00078) {
char buf[INPUT_BUFFER_SIZE];
dumb_read_misc_line(buf, "***MORE***");
} else
A00019();
}
void A00011(void)
{
if ((A00035 >= V4) && (speed != 0))
A00036 |= CONFIG_TIMEDINPUT;
if (A00035 >= V5)
A00044 &= ~(MOUSE_FLAG | MENU_FLAG);
}
zword os_read_mouse(void)
{
/* NOT IMPLEMENTED */
return 0;
}
void A00244()
{}