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