1 /*
2 * input.c
3 *
4 * Input routines
5 *
6 */
7
8 #include "ztypes.h"
9
10 /* Statically defined word separator list */
11
12 static const char *separators = " \t\n\f.,?";
13 static zword_t dictionary_offset = 0;
14 static short dictionary_size = 0;
15 static unsigned int entry_size = 0;
16
17 #ifdef __STDC__
18 static void tokenise_line (zword_t, zword_t, zword_t, zword_t);
19 static const char *next_token (const char *, const char *, const char **, int *, const char *);
20 static zword_t find_word (int, const char *, long);
21 #else
22 static void tokenise_line ();
23 static const char *next_token ();
24 static zword_t find_word ();
25 #endif
26
27 /*
28 * read_character
29 *
30 * Read one character with optional timeout
31 *
32 * argv[0] = # of characters to read (only 1 supported currently)
33 * argv[1] = timeout value in seconds (optional)
34 * argv[2] = timeout action routine (optional)
35 *
36 */
37
38 #ifdef __STDC__
read_character(int argc,zword_t * argv)39 void read_character (int argc, zword_t *argv)
40 #else
41 void read_character (argc, argv)
42 int argc;
43 zword_t *argv;
44 #endif
45 {
46 int c;
47 zword_t arg_list[2];
48
49 /* Supply default parameters */
50
51 if (argc < 3)
52 argv[2] = 0;
53 if (argc < 2)
54 argv[1] = 0;
55
56 /* Flush any buffered output before read */
57
58 flush_buffer (FALSE);
59
60 /* Reset line count */
61
62 lines_written = 0;
63
64 /* If more than one characters was asked for then fail the call */
65
66 if (argv[0] != 1)
67
68 c = 0;
69
70 else {
71
72 if ((c = playback_key ()) == -1) {
73
74 /* Setup the timeout routine argument list */
75
76 arg_list[0] = argv[2];
77 arg_list[1] = argv[1];
78
79 /* Read a character with a timeout. If the input timed out then
80 call the timeout action routine. If the return status from the
81 timeout routine was 0 then try to read a character again */
82
83 do {
84 c = input_character ((int) argv[1]);
85 } while (c == -1 && call (1, arg_list, ASYNC) == 0);
86
87 /* Fail call if input timed out */
88
89 if (c == -1)
90 c = 0;
91 else
92 record_key (c);
93 }
94 }
95
96 store_operand (c);
97
98 }/* read_character */
99
100 /*
101 * read_line
102 *
103 * Read a line of input with optional timeout.
104 *
105 * argv[0] = character buffer address
106 * argv[1] = token buffer address
107 * argv[2] = timeout value in seconds (optional)
108 * argv[3] = timeout action routine (optional)
109 *
110 */
111
112 #ifdef __STDC__
read_line(int argc,zword_t * argv)113 void read_line (int argc, zword_t *argv)
114 #else
115 void read_line (argc, argv)
116 int argc;
117 zword_t *argv;
118 #endif
119 {
120 int i, in_size, out_size, terminator;
121 char *cbuf, *buffer;
122
123 /* Supply default parameters */
124
125 if (argc < 4)
126 argv[3] = 0;
127 if (argc < 3)
128 argv[2] = 0;
129 if (argc < 2)
130 argv[1] = 0;
131
132 /* Refresh status line */
133
134 if (h_type < V4)
135 display_status_line ();
136
137 /* Flush any buffered output before read */
138
139 flush_buffer (TRUE);
140
141 /* Reset line count */
142
143 lines_written = 0;
144
145 /* Initialise character pointer and initial read size */
146
147 cbuf = (char *) &datap[argv[0]];
148 in_size = (h_type > V4) ? cbuf[1] : 0;
149
150 /* Read the line */
151
152 terminator = get_line (cbuf, argv[2], argv[3]);
153 if (h_type > V4) {
154 buffer = &cbuf[2];
155 out_size = (unsigned char)(cbuf[1]);
156 } else {
157 buffer = &cbuf[1];
158 out_size = strlen (buffer);
159 }
160
161 /* Then script and record it */
162
163 script_line (buffer, out_size);
164 record_line (buffer, out_size);
165
166 /* Convert new text in line to lowercase */
167
168 if (out_size > in_size)
169 for (i = in_size; i < out_size; i++)
170 buffer[i] = (char) tolower (buffer[i]);
171
172 /* Tokenise the line, if a token buffer is present */
173
174 if (argv[1])
175 tokenise_line (argv[0], argv[1], h_words_offset, 0);
176
177 /* Return the line terminator */
178
179 if (h_type > V4)
180 store_operand ((zword_t) terminator);
181
182 }/* read_line */
183
184 /*
185 * get_line
186 *
187 * Read a line of input and lower case it.
188 *
189 */
190
191 #ifdef __STDC__
get_line(char * cbuf,zword_t timeout,zword_t action_routine)192 int get_line (char *cbuf, zword_t timeout, zword_t action_routine)
193 #else
194 int get_line (cbuf, timeout, action_routine)
195 char *cbuf;
196 zword_t timeout;
197 zword_t action_routine;
198 #endif
199 {
200 char *buffer;
201 int buflen, read_size, status, c;
202 zword_t arg_list[2];
203
204 /* Set maximum buffer size to width of screen minus any
205 right margin and 1 character for a terminating NULL */
206
207 /*buflen = (screen_cols > 127) ? 127 : screen_cols;
208 buflen -= right_margin + 1;*/
209 buflen = 127; /* --zarf */
210
211 if ((unsigned char) cbuf[0] <= buflen)
212 buflen = (unsigned char) cbuf[0];
213
214 /* Set read size and start of read buffer. The buffer may already be
215 primed with same text in V5 games. The Z-code will have already
216 displayed the text so we don't have to do that */
217
218 if (h_type > V4) {
219 read_size = (unsigned char) cbuf[1];
220 buffer = &cbuf[2];
221 } else {
222 read_size = 0;
223 buffer = &cbuf[1];
224 }
225
226 /* Try to read input from command file */
227
228 c = playback_line (buflen, buffer, &read_size);
229
230 if (c == -1) {
231
232 /* Setup the timeout routine argument list */
233
234 arg_list[0] = action_routine;
235 arg_list[1] = timeout;
236
237 /* Read a line with a timeout. If the input timed out then
238 call the timeout action routine. If the return status from the
239 timeout routine was 0 then try to read the line again */
240
241 status = 1;
242 do {
243 c = input_line (buflen, buffer, timeout, &read_size, status);
244 status = 0;
245 } while (c == -1 && (status = call (1, arg_list, ASYNC)) == 0);
246
247 /* Throw away any input if timeout returns success */
248
249 if (status)
250 read_size = 0;
251
252 }
253
254 if (h_type > V4) {
255 cbuf[1] = (char) read_size;
256 }
257 else {
258 /* Zero terminate line (V1-4 only) */
259 buffer[read_size] = '\0';
260 }
261
262 return (c);
263
264 }/* get_line */
265
266 /*
267 * tokenise_line
268 *
269 * Convert a typed input line into tokens. The token buffer needs some
270 * additional explanation. The first byte is the maximum number of tokens
271 * allowed. The second byte is set to the actual number of token read. Each
272 * token is composed of 3 fields. The first (word) field contains the word
273 * offset in the dictionary, the second (byte) field contains the token length,
274 * and the third (byte) field contains the start offset of the token in the
275 * character buffer.
276 *
277 */
278
279 #ifdef __STDC__
tokenise_line(zword_t char_buf,zword_t token_buf,zword_t dictionary,zword_t flag)280 static void tokenise_line (zword_t char_buf, zword_t token_buf, zword_t dictionary, zword_t flag)
281 #else
282 static void tokenise_line (char_buf, token_buf, dictionary, flag)
283 zword_t char_buf;
284 zword_t token_buf;
285 zword_t dictionary;
286 zword_t flag;
287 #endif
288 {
289 int i, count, words, token_length;
290 long word_index, chop = 0;
291 int slen;
292 char *str_end;
293 char *cbuf, *tbuf, *tp;
294 const char *cp, *token;
295 char punctuation[16];
296 zword_t word;
297
298 /* Initialise character and token buffer pointers */
299
300 cbuf = (char *) &datap[char_buf];
301 tbuf = (char *) &datap[token_buf];
302
303 /* Find the string length */
304
305 if (h_type > V4) {
306 slen = (unsigned char)(cbuf[1]);
307 str_end = cbuf + 2 + slen;
308 }
309 else {
310 slen = strlen(cbuf+1);
311 str_end = cbuf + 1 + slen;
312 }
313
314 /* Initialise word count and pointers */
315
316 words = 0;
317 cp = (h_type > V4) ? cbuf + 2 : cbuf + 1;
318 tp = tbuf + 2;
319
320 /* Initialise dictionary */
321
322 count = get_byte (dictionary++);
323 for (i = 0; i < count; i++)
324 punctuation[i] = get_byte (dictionary++);
325 punctuation[i] = '\0';
326 entry_size = get_byte (dictionary++);
327 dictionary_size = (short) get_word (dictionary);
328 dictionary_offset = dictionary + 2;
329
330 /* Calculate the binary chop start position */
331
332 if (dictionary_size > 0) {
333 word_index = dictionary_size / 2;
334 chop = 1;
335 do
336 chop *= 2;
337 while (word_index /= 2);
338 }
339
340 /* Tokenise the line */
341
342 do {
343
344 /* Skip to next token */
345
346 cp = next_token (cp, str_end, &token, &token_length, punctuation);
347 if (token_length)
348
349 /* If still space in token buffer then store word */
350
351 if (words <= tbuf[0]) {
352
353 /* Get the word offset from the dictionary */
354
355 word = find_word (token_length, token, chop);
356
357 /* Store the dictionary offset, token length and offset */
358
359 if (word || flag == 0) {
360 tp[0] = (char) (word >> 8);
361 tp[1] = (char) (word & 0xff);
362 }
363 tp[2] = (char) token_length;
364 tp[3] = (char) (token - cbuf);
365
366 /* Step to next token position and count the word */
367
368 tp += 4;
369 words++;
370 } else {
371
372 /* Moan if token buffer space exhausted */
373
374 output_string ("Too many words typed, discarding: ");
375 output_line (token);
376 }
377 } while (token_length);
378
379 /* Store word count */
380
381 tbuf[1] = (char) words;
382
383 }/* tokenise_line */
384
385 /*
386 * next_token
387 *
388 * Find next token in a string. The token (word) is delimited by a statically
389 * defined and a game specific set of word separators. The game specific set
390 * of separators look like real word separators, but the parser wants to know
391 * about them. An example would be: 'grue, take the axe. go north'. The
392 * parser wants to know about the comma and the period so that it can correctly
393 * parse the line. The 'interesting' word separators normally appear at the
394 * start of the dictionary, and are also put in a separate list in the game
395 * file.
396 *
397 */
398
399 #ifdef __STDC__
next_token(const char * s,const char * str_end,const char ** token,int * length,const char * punctuation)400 static const char *next_token (const char *s, const char *str_end, const char **token, int *length, const char *punctuation)
401 #else
402 static const char *next_token (s, str_end, token, length, punctuation)
403 const char *s;
404 const char *str_end;
405 const char **token;
406 int *length;
407 const char *punctuation;
408 #endif
409 {
410 int i;
411
412 /* Set the token length to zero */
413
414 *length = 0;
415
416 /* Step through the string looking for separators */
417
418 for (; s < str_end; s++) {
419
420 /* Look for game specific word separators first */
421
422 for (i = 0; punctuation[i] && *s != punctuation[i]; i++)
423 ;
424
425 /* If a separator is found then return the information */
426
427 if (punctuation[i]) {
428
429 /* If length has been set then just return the word position */
430
431 if (*length)
432 return (s);
433 else {
434
435 /* End of word, so set length, token pointer and return string */
436
437 (*length)++;
438 *token = s;
439 return (++s);
440 }
441 }
442
443 /* Look for statically defined separators last */
444
445 for (i = 0; separators[i] && *s != separators[i]; i++)
446 ;
447
448 /* If a separator is found then return the information */
449
450 if (separators[i]) {
451
452 /* If length has been set then just return the word position */
453
454 if (*length)
455 return (++s);
456 } else {
457
458 /* If first token character then remember its position */
459
460 if (*length == 0)
461 *token = s;
462 (*length)++;
463 }
464 }
465
466 return (s);
467
468 }/* next_token */
469
470 /*
471 * find_word
472 *
473 * Search the dictionary for a word. Just encode the word and binary chop the
474 * dictionary looking for it.
475 *
476 */
477
478 #ifdef __STDC__
find_word(int len,const char * cp,long chop)479 static zword_t find_word (int len, const char *cp, long chop)
480 #else
481 static zword_t find_word (len, cp, chop)
482 int len;
483 const char *cp;
484 long chop;
485 #endif
486 {
487 short int word[3];
488 long word_index, offset, status;
489
490 /* Don't look up the word if there are no dictionary entries */
491
492 if (dictionary_size == 0)
493 return (0);
494
495 /* Encode target word */
496
497 encode_text (len, cp, word);
498
499 /* Do a binary chop search on the main dictionary, otherwise do
500 a linear search */
501
502 word_index = chop - 1;
503
504 if (dictionary_size > 0) {
505
506 /* Binary chop until the word is found */
507
508 while (chop) {
509
510 chop /= 2;
511
512 /* Calculate dictionary offset */
513
514 if (word_index > (dictionary_size - 1))
515 word_index = dictionary_size - 1;
516
517 offset = dictionary_offset + (word_index * entry_size);
518
519 /* If word matches then return dictionary offset */
520
521 if ((status = word[0] - (short) get_word (offset + 0)) == 0 &&
522 (status = word[1] - (short) get_word (offset + 2)) == 0 &&
523 (h_type < V4 ||
524 (status = word[2] - (short) get_word (offset + 4)) == 0))
525 return ((zword_t) offset);
526
527 /* Set next position depending on direction of overshoot */
528
529 if (status > 0) {
530 word_index += chop;
531
532 /* Deal with end of dictionary case */
533
534 if (word_index >= (int) dictionary_size)
535 word_index = dictionary_size - 1;
536 } else {
537 word_index -= chop;
538
539 /* Deal with start of dictionary case */
540
541 if (word_index < 0)
542 word_index = 0;
543 }
544 }
545 } else {
546
547 for (word_index = 0; word_index < -dictionary_size; word_index++) {
548
549 /* Calculate dictionary offset */
550
551 offset = dictionary_offset + (word_index * entry_size);
552
553 /* If word matches then return dictionary offset */
554
555 if ((status = word[0] - (short) get_word (offset + 0)) == 0 &&
556 (status = word[1] - (short) get_word (offset + 2)) == 0 &&
557 (h_type < V4 ||
558 (status = word[2] - (short) get_word (offset + 4)) == 0))
559 return ((zword_t) offset);
560 }
561 }
562
563 return (0);
564
565 }/* find_word */
566
567 /*
568 * tokenise
569 *
570 * argv[0] = character buffer address
571 * argv[1] = token buffer address
572 * argv[2] = alternate vocabulary table
573 * argv[3] = ignore unknown words flag
574 *
575 */
576
577 #ifdef __STDC__
tokenise(int argc,zword_t * argv)578 void tokenise (int argc, zword_t *argv)
579 #else
580 void tokenise (argc, argv)
581 int argc;
582 zword_t *argv;
583 #endif
584 {
585
586 /* Supply default parameters */
587
588 if (argc < 4)
589 argv[3] = 0;
590 if (argc < 3)
591 argv[2] = h_words_offset;
592
593 /* Convert the line to tokens */
594
595 tokenise_line (argv[0], argv[1], argv[2], argv[3]);
596
597 }/* tokenise */
598