2
/* $Id: input.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
3
* --------------------------------------------------------------------
4
* see doc/License.txt for License Information
5
* --------------------------------------------------------------------
7
* File name: $Id: input.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
11
* Modification history:
13
* Revision 1.3 2000/07/05 15:20:34 jholder
14
* Updated code to remove warnings.
16
* Revision 1.2 2000/05/25 22:28:56 jholder
17
* changes routine names to reflect zmachine opcode names per spec 1.0
19
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
24
* --------------------------------------------------------------------
36
/* Statically defined word separator list */
38
static const char *separators = " \t\n\f.,?";
39
static zword_t dictionary_offset = 0;
40
static ZINT16 dictionary_size = 0;
41
static unsigned int entry_size = 0;
43
static void tokenise_line( zword_t, zword_t, zword_t, zword_t );
44
static const char *next_token( const char *, const char *, const char **, int *, const char * );
45
static zword_t find_word( int, const char *, long );
50
* Read one character with optional timeout
52
* argv[0] = input device (must be 1)
53
* argv[1] = timeout value in tenths of a second (optional)
54
* argv[2] = timeout action routine (optional)
58
void z_read_char( int argc, zword_t * argv )
63
/* Supply default parameters */
70
/* Flush any buffered output before read */
72
flush_buffer( FALSE );
74
/* Reset line count */
78
/* If more than one characters was asked for then fail the call */
87
if ( ( c = playback_key( ) ) == -1 )
90
/* Setup the timeout routine argument list */
92
arg_list[0] = argv[2];
93
arg_list[1] = 0; /* as per spec 1.0 */
94
/* was: arg_list[1] = argv[1]/10; */
96
/* Read a character with a timeout. If the input timed out then
97
* call the timeout action routine. If the return status from the
98
* timeout routine was 0 then try to read a character again */
102
flush_buffer( FALSE );
103
c = input_character( ( int ) argv[1] );
105
while ( c == -1 && z_call( 1, arg_list, ASYNC ) == 0 );
107
/* Fail call if input timed out */
116
store_operand( (zword_t)c );
123
* Read a line of input with optional timeout.
125
* argv[0] = character buffer address
126
* argv[1] = token buffer address
127
* argv[2] = timeout value in seconds (optional)
128
* argv[3] = timeout action routine (optional)
132
void z_sread_aread( int argc, zword_t * argv )
134
int i, in_size, out_size, terminator;
137
/* Supply default parameters */
146
/* Refresh status line */
151
/* Flush any buffered output before read */
153
flush_buffer( TRUE );
155
/* Reset line count */
159
/* Initialise character pointer and initial read size */
161
cbuf = ( char * ) &datap[argv[0]];
162
in_size = ( h_type > V4 ) ? cbuf[1] : 0;
164
/* Read the line then script and record it */
166
terminator = get_line( cbuf, argv[2], argv[3] );
167
script_line( ( h_type > V4 ) ? &cbuf[2] : &cbuf[1] );
168
record_line( ( h_type > V4 ) ? &cbuf[2] : &cbuf[1] );
170
/* Convert new text in line to lowercase */
180
out_size = strlen( buffer );
183
if ( out_size > in_size )
184
for ( i = in_size; i < out_size; i++ )
185
buffer[i] = ( char ) tolower( buffer[i] );
187
/* Tokenise the line, if a token buffer is present */
190
tokenise_line( argv[0], argv[1], h_words_offset, 0 );
192
/* Return the line terminator */
195
store_operand( ( zword_t ) terminator );
197
} /* z_sread_aread */
202
* Read a line of input and lower case it.
206
int get_line( char *cbuf, zword_t timeout, zword_t action_routine )
209
int buflen, read_size, status, c;
212
/* Set maximum buffer size to width of screen minus any
213
* right margin and 1 character for a terminating NULL */
215
buflen = ( screen_cols > 127 ) ? 127 : screen_cols;
216
buflen -= right_margin + 1;
217
if ( ( int ) cbuf[0] <= buflen )
220
/* Set read size and start of read buffer. The buffer may already be
221
* primed with some text in V5 games. The Z-code will have already
222
* displayed the text so we don't have to do that */
235
/* Try to read input from command file */
237
c = playback_line( buflen, buffer, &read_size );
242
/* Setup the timeout routine argument list */
244
arg_list[0] = action_routine;
245
arg_list[1] = 0; /* as per spec.1.0 */
246
/* arg_list[1] = timeout/10; */
248
/* Read a line with a timeout. If the input timed out then
249
* call the timeout action routine. If the return status from the
250
* timeout routine was 0 then try to read the line again */
254
c = input_line( buflen, buffer, timeout, &read_size );
257
while ( c == -1 && ( status = z_call( 1, arg_list, ASYNC ) ) == 0 );
259
/* Throw away any input if timeout returns success */
267
/* Zero terminate line */
271
cbuf[1] = ( char ) read_size;
275
/* Zero terminate line (V1-4 only) */
276
buffer[read_size] = '\0';
286
* Convert a typed input line into tokens. The token buffer needs some
287
* additional explanation. The first byte is the maximum number of tokens
288
* allowed. The second byte is set to the actual number of token read. Each
289
* token is composed of 3 fields. The first (word) field contains the word
290
* offset in the dictionary, the second (byte) field contains the token length,
291
* and the third (byte) field contains the start offset of the token in the
296
static void tokenise_line( zword_t char_buf, zword_t token_buf, zword_t dictionary, zword_t flag )
298
int i, count, words, token_length;
299
long word_index, chop = 0;
302
char *cbuf, *tbuf, *tp;
303
const char *cp, *token;
304
char punctuation[16];
307
/* Initialise character and token buffer pointers */
309
cbuf = ( char * ) &datap[char_buf];
310
tbuf = ( char * ) &datap[token_buf];
312
/* Find the string length */
316
slen = ( unsigned char ) ( cbuf[1] );
317
str_end = cbuf + 2 + slen;
321
slen = strlen( cbuf + 1 );
322
str_end = cbuf + 1 + slen;
325
/* Initialise word count and pointers */
328
cp = ( h_type > V4 ) ? cbuf + 2 : cbuf + 1;
331
/* Initialise dictionary */
333
count = get_byte( dictionary++ );
334
for ( i = 0; i < count; i++ )
335
punctuation[i] = get_byte( dictionary++ );
336
punctuation[i] = '\0';
337
entry_size = get_byte( dictionary++ );
338
dictionary_size = ( ZINT16 ) get_word( dictionary );
339
dictionary_offset = dictionary + 2;
341
/* Calculate the binary chop start position */
343
if ( dictionary_size > 0 )
345
word_index = dictionary_size / 2;
349
while ( word_index /= 2 );
352
/* Tokenise the line */
357
/* Skip to next token */
359
cp = next_token( cp, str_end, &token, &token_length, punctuation );
362
/* If still space in token buffer then store word */
364
if ( words <= tbuf[0] )
367
/* Get the word offset from the dictionary */
369
word = find_word( token_length, token, chop );
371
/* Store the dictionary offset, token length and offset */
373
if ( word || flag == 0 )
375
tp[0] = ( char ) ( word >> 8 );
376
tp[1] = ( char ) ( word & 0xff );
378
tp[2] = ( char ) token_length;
379
tp[3] = ( char ) ( token - cbuf );
381
/* Step to next token position and count the word */
389
/* Moan if token buffer space exhausted */
391
output_string( "Too many words typed, discarding: " );
392
output_line( token );
395
while ( token_length );
397
/* Store word count */
399
tbuf[1] = ( char ) words;
401
} /* tokenise_line */
406
* Find next token in a string. The token (word) is delimited by a statically
407
* defined and a game specific set of word separators. The game specific set
408
* of separators look like real word separators, but the parser wants to know
409
* about them. An example would be: 'grue, take the axe. go north'. The
410
* parser wants to know about the comma and the period so that it can correctly
411
* parse the line. The 'interesting' word separators normally appear at the
412
* start of the dictionary, and are also put in a separate list in the game
417
static const char *next_token( const char *s, const char *str_end, const char **token, int *length,
418
const char *punctuation )
422
/* Set the token length to zero */
426
/* Step through the string looking for separators */
428
for ( ; s < str_end; s++ )
431
/* Look for game specific word separators first */
433
for ( i = 0; punctuation[i] && *s != punctuation[i]; i++ )
436
/* If a separator is found then return the information */
438
if ( punctuation[i] )
441
/* If length has been set then just return the word position */
448
/* End of word, so set length, token pointer and return string */
456
/* Look for statically defined separators last */
458
for ( i = 0; separators[i] && *s != separators[i]; i++ )
461
/* If a separator is found then return the information */
466
/* If length has been set then just return the word position */
474
/* If first token character then remember its position */
489
* Search the dictionary for a word. Just encode the word and binary chop the
490
* dictionary looking for it.
494
static zword_t find_word( int len, const char *cp, long chop )
497
long word_index, offset, status;
499
/* Don't look up the word if there are no dictionary entries */
501
if ( dictionary_size == 0 )
504
/* Encode target word */
506
encode_text( len, cp, word );
508
/* Do a binary chop search on the main dictionary, otherwise do
511
word_index = chop - 1;
513
if ( dictionary_size > 0 )
516
/* Binary chop until the word is found */
523
/* Calculate dictionary offset */
525
if ( word_index > ( dictionary_size - 1 ) )
526
word_index = dictionary_size - 1;
528
offset = dictionary_offset + ( word_index * entry_size );
530
/* If word matches then return dictionary offset */
532
if ( ( status = word[0] - ( ZINT16 ) get_word( offset + 0 ) ) == 0 &&
533
( status = word[1] - ( ZINT16 ) get_word( offset + 2 ) ) == 0 &&
534
( h_type < V4 || ( status = word[2] - ( ZINT16 ) get_word( offset + 4 ) ) == 0 ) )
535
return ( ( zword_t ) offset );
537
/* Set next position depending on direction of overshoot */
543
/* Deal with end of dictionary case */
545
if ( word_index >= ( int ) dictionary_size )
546
word_index = dictionary_size - 1;
552
/* Deal with start of dictionary case */
554
if ( word_index < 0 )
562
for ( word_index = 0; word_index < -dictionary_size; word_index++ )
565
/* Calculate dictionary offset */
567
offset = dictionary_offset + ( word_index * entry_size );
569
/* If word matches then return dictionary offset */
571
if ( ( status = word[0] - ( ZINT16 ) get_word( offset + 0 ) ) == 0 &&
572
( status = word[1] - ( ZINT16 ) get_word( offset + 2 ) ) == 0 &&
573
( h_type < V4 || ( status = word[2] - ( ZINT16 ) get_word( offset + 4 ) ) == 0 ) )
574
return ( ( zword_t ) offset );
585
* argv[0] = character buffer address
586
* argv[1] = token buffer address
587
* argv[2] = alternate vocabulary table
588
* argv[3] = ignore unknown words flag
592
void z_tokenise( int argc, zword_t * argv )
595
/* Supply default parameters */
600
argv[2] = h_words_offset;
602
/* Convert the line to tokens */
604
tokenise_line( argv[0], argv[1], argv[2], argv[3] );