diff options
Diffstat (limited to 'engines/glk/adrift/scparser.cpp')
-rw-r--r-- | engines/glk/adrift/scparser.cpp | 3170 |
1 files changed, 1508 insertions, 1662 deletions
diff --git a/engines/glk/adrift/scparser.cpp b/engines/glk/adrift/scparser.cpp index 6f7642c3a7..9372b5e39e 100644 --- a/engines/glk/adrift/scparser.cpp +++ b/engines/glk/adrift/scparser.cpp @@ -48,37 +48,35 @@ static const sc_char *const WHITESPACE = "\t\n\v\f\r "; static sc_bool uip_trace = FALSE; /* Enumeration of tokens. TOK_NONE represents a non-occurring token. */ -typedef enum -{ - TOK_NONE = 0, - TOK_CHOICE, TOK_CHOICE_END, TOK_OPTIONAL, TOK_OPTIONAL_END, - TOK_ALTERNATES_SEPARATOR, - TOK_WILDCARD, TOK_WHITESPACE, TOK_WORD, TOK_VARIABLE, - TOK_CHARACTER_REFERENCE, TOK_OBJECT_REFERENCE, TOK_NUMBER_REFERENCE, - TOK_TEXT_REFERENCE, TOK_EOS +typedef enum { + TOK_NONE = 0, + TOK_CHOICE, TOK_CHOICE_END, TOK_OPTIONAL, TOK_OPTIONAL_END, + TOK_ALTERNATES_SEPARATOR, + TOK_WILDCARD, TOK_WHITESPACE, TOK_WORD, TOK_VARIABLE, + TOK_CHARACTER_REFERENCE, TOK_OBJECT_REFERENCE, TOK_NUMBER_REFERENCE, + TOK_TEXT_REFERENCE, TOK_EOS } sc_uip_tok_t; /* * Small table tying token strings to tokens. Anything not whitespace and * not caught by the table is a plain TOK_WORD. */ -typedef struct -{ - const sc_char *const name; - const sc_int length; - const sc_uip_tok_t token; +typedef struct { + const sc_char *const name; + const sc_int length; + const sc_uip_tok_t token; } sc_uip_token_entry_t; static const sc_uip_token_entry_t UIP_TOKENS[] = { - {"[", 1, TOK_CHOICE}, {"]", 1, TOK_CHOICE_END}, - {"{", 1, TOK_OPTIONAL}, {"}", 1, TOK_OPTIONAL_END}, - {"/", 1, TOK_ALTERNATES_SEPARATOR}, - {"*", 1, TOK_WILDCARD}, - {"%character%", 11, TOK_CHARACTER_REFERENCE}, - {"%object%", 8, TOK_OBJECT_REFERENCE}, - {"%number%", 8, TOK_NUMBER_REFERENCE}, - {"%text%", 6, TOK_TEXT_REFERENCE}, - {NULL, 0, TOK_NONE} + {"[", 1, TOK_CHOICE}, {"]", 1, TOK_CHOICE_END}, + {"{", 1, TOK_OPTIONAL}, {"}", 1, TOK_OPTIONAL_END}, + {"/", 1, TOK_ALTERNATES_SEPARATOR}, + {"*", 1, TOK_WILDCARD}, + {"%character%", 11, TOK_CHARACTER_REFERENCE}, + {"%object%", 8, TOK_OBJECT_REFERENCE}, + {"%number%", 8, TOK_NUMBER_REFERENCE}, + {"%text%", 6, TOK_TEXT_REFERENCE}, + {NULL, 0, TOK_NONE} }; @@ -101,49 +99,44 @@ static sc_char *uip_temporary = NULL; * Start and wrap up pattern string tokenization. */ static void -uip_tokenize_start (const sc_char *pattern) -{ - static sc_bool initialized = FALSE; - sc_int required; - - /* On first call only, verify the string lengths in the table. */ - if (!initialized) - { - const sc_uip_token_entry_t *entry; - - /* Compare table lengths with string lengths. */ - for (entry = UIP_TOKENS; entry->name; entry++) - { - if (entry->length != (sc_int) strlen (entry->name)) - { - sc_fatal ("uip_tokenize_start:" - " table string length is wrong for \"%s\"\n", - entry->name); - } - } - - initialized = TRUE; - } - - /* Save pattern, and restart index. */ - uip_pattern = pattern; - uip_index = 0; - - /* Set up temporary; static if long enough, otherwise allocated. */ - required = strlen (pattern) + 1; - uip_temporary = (required > UIP_ALLOCATION_AVOIDANCE_SIZE) - ? (sc_char *)sc_malloc (required) : uip_static_temporary; +uip_tokenize_start(const sc_char *pattern) { + static sc_bool initialized = FALSE; + sc_int required; + + /* On first call only, verify the string lengths in the table. */ + if (!initialized) { + const sc_uip_token_entry_t *entry; + + /* Compare table lengths with string lengths. */ + for (entry = UIP_TOKENS; entry->name; entry++) { + if (entry->length != (sc_int) strlen(entry->name)) { + sc_fatal("uip_tokenize_start:" + " table string length is wrong for \"%s\"\n", + entry->name); + } + } + + initialized = TRUE; + } + + /* Save pattern, and restart index. */ + uip_pattern = pattern; + uip_index = 0; + + /* Set up temporary; static if long enough, otherwise allocated. */ + required = strlen(pattern) + 1; + uip_temporary = (required > UIP_ALLOCATION_AVOIDANCE_SIZE) + ? (sc_char *)sc_malloc(required) : uip_static_temporary; } static void -uip_tokenize_end (void) -{ - /* Deallocate temporary if required, and clear pattern and index. */ - if (uip_temporary != uip_static_temporary) - sc_free (uip_temporary); - uip_temporary = NULL; - uip_pattern = NULL; - uip_index = 0; +uip_tokenize_end(void) { + /* Deallocate temporary if required, and clear pattern and index. */ + if (uip_temporary != uip_static_temporary) + sc_free(uip_temporary); + uip_temporary = NULL; + uip_pattern = NULL; + uip_index = 0; } @@ -153,68 +146,62 @@ uip_tokenize_end (void) * Return the next token from the current pattern. */ static sc_uip_tok_t -uip_next_token (void) -{ - const sc_uip_token_entry_t *entry; - sc_char close; - assert (uip_pattern); - - /* Get next character, return EOS if at pattern end. */ - if (uip_pattern[uip_index] == NUL) - { - uip_token_value = NULL; - return TOK_EOS; - } - - /* If whitespace, skip it, then return a whitespace token. */ - if (sc_isspace (uip_pattern[uip_index])) - { - uip_index++; - while (sc_isspace (uip_pattern[uip_index]) - && uip_pattern[uip_index] != NUL) - uip_index++; - uip_token_value = NULL; - return TOK_WHITESPACE; - } - - /* Search the table for matching strings. */ - for (entry = UIP_TOKENS; entry->name; entry++) - { - if (strncmp (uip_pattern + uip_index, entry->name, entry->length) == 0) - break; - } - if (entry->name) - { - /* Advance over string, and return token. */ - uip_index += entry->length; - uip_token_value = NULL; - return entry->token; - } - - /* - * Search for a non-special variable reference. This is apparently an - * Adrift extension to the standard pattern match, allowing %user_var% to - * be used in patterns. If found, return a variable with the name as the - * token value. We can't interpolate the value into the string either - * here or earlier, so we have to save the variable's name, and retrieve - * it when we come to try the match. - */ - if (sscanf (uip_pattern + uip_index, "%%%[^%]%c", uip_temporary, &close) == 2 - && close == PERCENT) - { - uip_index += strlen (uip_temporary) + 2; - uip_token_value = uip_temporary; - return TOK_VARIABLE; - } - - /* - * Return a word. This is a contiguous run of non-pattern-special, non- - * whitespace, non-percent characters - */ - sscanf (uip_pattern + uip_index, "%[^][/{}*% \f\n\r\t\v]", uip_temporary); - uip_token_value = uip_temporary; - uip_index += strlen (uip_temporary); - return TOK_WORD; +uip_next_token(void) { + const sc_uip_token_entry_t *entry; + sc_char close; + assert(uip_pattern); + + /* Get next character, return EOS if at pattern end. */ + if (uip_pattern[uip_index] == NUL) { + uip_token_value = NULL; + return TOK_EOS; + } + + /* If whitespace, skip it, then return a whitespace token. */ + if (sc_isspace(uip_pattern[uip_index])) { + uip_index++; + while (sc_isspace(uip_pattern[uip_index]) + && uip_pattern[uip_index] != NUL) + uip_index++; + uip_token_value = NULL; + return TOK_WHITESPACE; + } + + /* Search the table for matching strings. */ + for (entry = UIP_TOKENS; entry->name; entry++) { + if (strncmp(uip_pattern + uip_index, entry->name, entry->length) == 0) + break; + } + if (entry->name) { + /* Advance over string, and return token. */ + uip_index += entry->length; + uip_token_value = NULL; + return entry->token; + } + + /* + * Search for a non-special variable reference. This is apparently an + * Adrift extension to the standard pattern match, allowing %user_var% to + * be used in patterns. If found, return a variable with the name as the + * token value. We can't interpolate the value into the string either + * here or earlier, so we have to save the variable's name, and retrieve + * it when we come to try the match. + */ + if (sscanf(uip_pattern + uip_index, "%%%[^%]%c", uip_temporary, &close) == 2 + && close == PERCENT) { + uip_index += strlen(uip_temporary) + 2; + uip_token_value = uip_temporary; + return TOK_VARIABLE; + } + + /* + * Return a word. This is a contiguous run of non-pattern-special, non- + * whitespace, non-percent characters + */ + sscanf(uip_pattern + uip_index, "%[^][/{}*% \f\n\r\t\v]", uip_temporary); + uip_token_value = uip_temporary; + uip_index += strlen(uip_temporary); + return TOK_WORD; } @@ -225,17 +212,15 @@ uip_next_token (void) * here if the current token is not a TOK_WORD or TOK_VARIABLE. */ static const sc_char * -uip_current_token_value (void) -{ - /* If the token value is NULL, the current token isn't a word. */ - if (!uip_token_value) - { - sc_fatal ("uip_current_token_value:" - " attempt to take undefined token value\n"); - } - - /* Return value. */ - return uip_token_value; +uip_current_token_value(void) { + /* If the token value is NULL, the current token isn't a word. */ + if (!uip_token_value) { + sc_fatal("uip_current_token_value:" + " attempt to take undefined token value\n"); + } + + /* Return value. */ + return uip_token_value; } @@ -245,21 +230,19 @@ uip_current_token_value (void) * NODE_UNUSED must be zero to ensure that the statically allocated array that * forms the node pool appears initially as containing only unused nodes. */ -typedef enum -{ - NODE_UNUSED = 0, - NODE_CHOICE, NODE_OPTIONAL, NODE_WILDCARD, NODE_WHITESPACE, - NODE_CHARACTER_REFERENCE, NODE_OBJECT_REFERENCE, NODE_TEXT_REFERENCE, - NODE_NUMBER_REFERENCE, NODE_WORD, NODE_VARIABLE, NODE_LIST, NODE_EOS +typedef enum { + NODE_UNUSED = 0, + NODE_CHOICE, NODE_OPTIONAL, NODE_WILDCARD, NODE_WHITESPACE, + NODE_CHARACTER_REFERENCE, NODE_OBJECT_REFERENCE, NODE_TEXT_REFERENCE, + NODE_NUMBER_REFERENCE, NODE_WORD, NODE_VARIABLE, NODE_LIST, NODE_EOS } sc_pttype_t; -typedef struct sc_ptnode_s -{ - struct sc_ptnode_s *left_child; - struct sc_ptnode_s *right_sibling; - - sc_pttype_t type; - sc_char *word; - sc_bool is_allocated; +typedef struct sc_ptnode_s { + struct sc_ptnode_s *left_child; + struct sc_ptnode_s *right_sibling; + + sc_pttype_t type; + sc_char *word; + sc_bool is_allocated; } sc_ptnode_t; typedef sc_ptnode_t *sc_ptnoderef_t; @@ -271,7 +254,7 @@ static jmp_buf uip_parse_error; /* Parse tree for cleanup, and forward declaration of pattern list parser. */ static sc_ptnoderef_t uip_parse_tree = NULL; -static void uip_parse_list (sc_ptnoderef_t list); +static void uip_parse_list(sc_ptnoderef_t list); /* * Pool of statically allocated nodes, for faster allocations. Nodes are @@ -290,10 +273,9 @@ static sc_int uip_node_pool_available = UIP_NODE_POOL_SIZE; * first, then by straight malloc() should the pool empty. */ enum { UIP_WORD_POOL_SIZE = 64, UIP_SHORT_WORD_SIZE = 16 }; -typedef struct -{ - sc_bool is_in_use; - sc_char word[UIP_SHORT_WORD_SIZE]; +typedef struct { + sc_bool is_in_use; + sc_char word[UIP_SHORT_WORD_SIZE]; } sc_ptshortword_t; typedef sc_ptshortword_t *sc_ptshortwordref_t; static sc_ptshortword_t uip_word_pool[UIP_WORD_POOL_SIZE]; @@ -306,17 +288,15 @@ static sc_int uip_word_pool_available = UIP_WORD_POOL_SIZE; * Match a token to the lookahead, then advance lookahead. */ static void -uip_parse_match (sc_uip_tok_t token) -{ - if (uip_parse_lookahead == token) - uip_parse_lookahead = uip_next_token (); - else - { - /* Syntax error. */ - sc_error ("uip_parse_match: syntax error, expected %ld, got %ld\n", - (sc_int) uip_parse_lookahead, (sc_int) token); - longjmp (uip_parse_error, 1); - } +uip_parse_match(sc_uip_tok_t token) { + if (uip_parse_lookahead == token) + uip_parse_lookahead = uip_next_token(); + else { + /* Syntax error. */ + sc_error("uip_parse_match: syntax error, expected %ld, got %ld\n", + (sc_int) uip_parse_lookahead, (sc_int) token); + longjmp(uip_parse_error, 1); + } } @@ -328,51 +308,46 @@ uip_parse_match (sc_uip_tok_t token) * exhausted, backs off to standard allocation. */ static sc_char * -uip_new_word (const sc_char *word) -{ - sc_int required; - - /* - * Unless the pool is empty, search forwards from the next cursor position - * until an unused slot is found, or until the index wraps to the cursor. - */ - required = strlen (word) + 1; - if (uip_word_pool_available > 0 && required <= UIP_SHORT_WORD_SIZE) - { - sc_int index_; - sc_ptshortwordref_t shortword; - - index_ = (uip_word_pool_cursor + 1) % UIP_WORD_POOL_SIZE; - while (index_ != uip_word_pool_cursor) - { - if (!uip_word_pool[index_].is_in_use) - break; - index_ = (index_ + 1) % UIP_WORD_POOL_SIZE; - } - - if (uip_word_pool[index_].is_in_use) - sc_fatal ("uip_new_word: no free slot found in the words pool\n"); - - /* Use the slot and update the pool cursor and free count. */ - shortword = uip_word_pool + index_; - strcpy (shortword->word, word); - shortword->is_in_use = TRUE; - - uip_word_pool_cursor = index_; - uip_word_pool_available--; - - /* Return the address of the copied string. */ - return shortword->word; - } - else - { - sc_char *word_copy; - - /* Fall back to less efficient allocations. */ - word_copy = (sc_char *)sc_malloc (required); - strcpy (word_copy, word); - return word_copy; - } +uip_new_word(const sc_char *word) { + sc_int required; + + /* + * Unless the pool is empty, search forwards from the next cursor position + * until an unused slot is found, or until the index wraps to the cursor. + */ + required = strlen(word) + 1; + if (uip_word_pool_available > 0 && required <= UIP_SHORT_WORD_SIZE) { + sc_int index_; + sc_ptshortwordref_t shortword; + + index_ = (uip_word_pool_cursor + 1) % UIP_WORD_POOL_SIZE; + while (index_ != uip_word_pool_cursor) { + if (!uip_word_pool[index_].is_in_use) + break; + index_ = (index_ + 1) % UIP_WORD_POOL_SIZE; + } + + if (uip_word_pool[index_].is_in_use) + sc_fatal("uip_new_word: no free slot found in the words pool\n"); + + /* Use the slot and update the pool cursor and free count. */ + shortword = uip_word_pool + index_; + strcpy(shortword->word, word); + shortword->is_in_use = TRUE; + + uip_word_pool_cursor = index_; + uip_word_pool_available--; + + /* Return the address of the copied string. */ + return shortword->word; + } else { + sc_char *word_copy; + + /* Fall back to less efficient allocations. */ + word_copy = (sc_char *)sc_malloc(required); + strcpy(word_copy, word); + return word_copy; + } } @@ -383,33 +358,30 @@ uip_new_word (const sc_char *word) * pool entry and return it to the pool. */ static void -uip_free_word (sc_char *word) -{ - const sc_char *first_in_pool, *last_in_pool; - - /* Obtain the range of valid addresses for words from the word pool. */ - first_in_pool = uip_word_pool[0].word; - last_in_pool = uip_word_pool[UIP_WORD_POOL_SIZE - 1].word; - - /* If from the pool, mark the entry as no longer in use, otherwise free. */ - if (word >= first_in_pool && word <= last_in_pool) - { - sc_int index_; - sc_ptshortwordref_t shortword; - - /* - * Calculate the index to the word pool entry from which this short - * word was allocated. - */ - index_ = (word - first_in_pool) / sizeof (uip_word_pool[0]); - shortword = uip_word_pool + index_; - assert (shortword->word == word); - - shortword->is_in_use = FALSE; - uip_word_pool_available++; - } - else - sc_free (word); +uip_free_word(sc_char *word) { + const sc_char *first_in_pool, *last_in_pool; + + /* Obtain the range of valid addresses for words from the word pool. */ + first_in_pool = uip_word_pool[0].word; + last_in_pool = uip_word_pool[UIP_WORD_POOL_SIZE - 1].word; + + /* If from the pool, mark the entry as no longer in use, otherwise free. */ + if (word >= first_in_pool && word <= last_in_pool) { + sc_int index_; + sc_ptshortwordref_t shortword; + + /* + * Calculate the index to the word pool entry from which this short + * word was allocated. + */ + index_ = (word - first_in_pool) / sizeof(uip_word_pool[0]); + shortword = uip_word_pool + index_; + assert(shortword->word == word); + + shortword->is_in_use = FALSE; + uip_word_pool_available++; + } else + sc_free(word); } @@ -421,50 +393,45 @@ uip_free_word (sc_char *word) * exhausted, backs off to standard allocation. */ static sc_ptnoderef_t -uip_new_node (sc_pttype_t type) -{ - sc_ptnoderef_t node; - - /* - * Unless the pool is empty, search forwards from the next cursor position - * until an unused slot is found, or until the index wraps to the cursor. - */ - if (uip_node_pool_available > 0) - { - sc_int index_; - - index_ = (uip_node_pool_cursor + 1) % UIP_NODE_POOL_SIZE; - while (index_ != uip_node_pool_cursor) - { - if (uip_node_pool[index_].type == NODE_UNUSED) - break; - index_ = (index_ + 1) % UIP_NODE_POOL_SIZE; - } - - if (uip_node_pool[index_].type != NODE_UNUSED) - sc_fatal ("uip_new_node: no free slot found in the nodes pool\n"); - - /* Use the slot and update the pool cursor and free count. */ - node = uip_node_pool + index_; - node->is_allocated = FALSE; - - uip_node_pool_cursor = index_; - uip_node_pool_available--; - } - else - { - /* Fall back to less efficient allocations. */ - node = (sc_ptnoderef_t)sc_malloc(sizeof (*node)); - node->is_allocated = TRUE; - } - - /* Fill in the remaining fields and return the new node. */ - node->left_child = NULL; - node->right_sibling = NULL; - node->type = type; - node->word = NULL; - - return node; +uip_new_node(sc_pttype_t type) { + sc_ptnoderef_t node; + + /* + * Unless the pool is empty, search forwards from the next cursor position + * until an unused slot is found, or until the index wraps to the cursor. + */ + if (uip_node_pool_available > 0) { + sc_int index_; + + index_ = (uip_node_pool_cursor + 1) % UIP_NODE_POOL_SIZE; + while (index_ != uip_node_pool_cursor) { + if (uip_node_pool[index_].type == NODE_UNUSED) + break; + index_ = (index_ + 1) % UIP_NODE_POOL_SIZE; + } + + if (uip_node_pool[index_].type != NODE_UNUSED) + sc_fatal("uip_new_node: no free slot found in the nodes pool\n"); + + /* Use the slot and update the pool cursor and free count. */ + node = uip_node_pool + index_; + node->is_allocated = FALSE; + + uip_node_pool_cursor = index_; + uip_node_pool_available--; + } else { + /* Fall back to less efficient allocations. */ + node = (sc_ptnoderef_t)sc_malloc(sizeof(*node)); + node->is_allocated = TRUE; + } + + /* Fill in the remaining fields and return the new node. */ + node->left_child = NULL; + node->right_sibling = NULL; + node->type = type; + node->word = NULL; + + return node; } @@ -475,27 +442,23 @@ uip_new_node (sc_pttype_t type) * free its memory; if not, return it to the pool. */ static void -uip_destroy_node (sc_ptnoderef_t node) -{ - /* Free any word contained at this node. */ - if (node->word) - uip_free_word (node->word); - - /* - * If the node was allocated, poison memory and free it. If it came from - * the node pool, set it to unused and update the availability count for - * the pool. - */ - if (node->is_allocated) - { - memset (node, 0xaa, sizeof (*node)); - sc_free (node); - } - else - { - node->type = NODE_UNUSED; - uip_node_pool_available++; - } +uip_destroy_node(sc_ptnoderef_t node) { + /* Free any word contained at this node. */ + if (node->word) + uip_free_word(node->word); + + /* + * If the node was allocated, poison memory and free it. If it came from + * the node pool, set it to unused and update the availability count for + * the pool. + */ + if (node->is_allocated) { + memset(node, 0xaa, sizeof(*node)); + sc_free(node); + } else { + node->type = NODE_UNUSED; + uip_node_pool_available++; + } } @@ -507,30 +470,27 @@ uip_destroy_node (sc_ptnoderef_t node) * first function is a helper, returning a newly constructed parsed list. */ static sc_ptnoderef_t -uip_parse_new_list (void) -{ - sc_ptnoderef_t list; - - /* Create a new list node, parse into it, and return it. */ - list = uip_new_node (NODE_LIST); - uip_parse_list (list); - return list; +uip_parse_new_list(void) { + sc_ptnoderef_t list; + + /* Create a new list node, parse into it, and return it. */ + list = uip_new_node(NODE_LIST); + uip_parse_list(list); + return list; } static void -uip_parse_alternatives (sc_ptnoderef_t node) -{ - sc_ptnoderef_t child; - - /* Parse initial alternative, then add other listed alternatives. */ - node->left_child = uip_parse_new_list (); - child = node->left_child; - while (uip_parse_lookahead == TOK_ALTERNATES_SEPARATOR) - { - uip_parse_match (TOK_ALTERNATES_SEPARATOR); - child->right_sibling = uip_parse_new_list (); - child = child->right_sibling; - } +uip_parse_alternatives(sc_ptnoderef_t node) { + sc_ptnoderef_t child; + + /* Parse initial alternative, then add other listed alternatives. */ + node->left_child = uip_parse_new_list(); + child = node->left_child; + while (uip_parse_lookahead == TOK_ALTERNATES_SEPARATOR) { + uip_parse_match(TOK_ALTERNATES_SEPARATOR); + child->right_sibling = uip_parse_new_list(); + child = child->right_sibling; + } } @@ -540,107 +500,102 @@ uip_parse_alternatives (sc_ptnoderef_t node) * Parse a single pattern element. */ static sc_ptnoderef_t -uip_parse_element (void) -{ - sc_ptnoderef_t node = NULL; - sc_uip_tok_t token; - - /* Handle pattern element based on lookahead token. */ - switch (uip_parse_lookahead) - { - case TOK_WHITESPACE: - uip_parse_match (TOK_WHITESPACE); - node = uip_new_node (NODE_WHITESPACE); - break; - - case TOK_CHOICE: - /* Parse a [...[/.../...]] choice. */ - uip_parse_match (TOK_CHOICE); - node = uip_new_node (NODE_CHOICE); - uip_parse_alternatives (node); - uip_parse_match (TOK_CHOICE_END); - break; - - case TOK_OPTIONAL: - /* Parse a {...[/.../...]} optional element. */ - uip_parse_match (TOK_OPTIONAL); - node = uip_new_node (NODE_OPTIONAL); - uip_parse_alternatives (node); - uip_parse_match (TOK_OPTIONAL_END); - break; - - case TOK_WILDCARD: - case TOK_CHARACTER_REFERENCE: - case TOK_OBJECT_REFERENCE: - case TOK_NUMBER_REFERENCE: - case TOK_TEXT_REFERENCE: - /* Parse %mumble% references and * wildcards. */ - token = uip_parse_lookahead; - uip_parse_match (token); - switch (token) - { - case TOK_WILDCARD: - node = uip_new_node (NODE_WILDCARD); - break; - case TOK_CHARACTER_REFERENCE: - node = uip_new_node (NODE_CHARACTER_REFERENCE); - break; - case TOK_OBJECT_REFERENCE: - node = uip_new_node (NODE_OBJECT_REFERENCE); - break; - case TOK_NUMBER_REFERENCE: - node = uip_new_node (NODE_NUMBER_REFERENCE); - break; - case TOK_TEXT_REFERENCE: - node = uip_new_node (NODE_TEXT_REFERENCE); - break; - default: - sc_fatal ("uip_parse_element: invalid token, %ld\n", (sc_int) token); - } - break; - - case TOK_WORD: - { - const sc_char *token_value; - sc_char *word; - - /* Take a copy of the token's word value. */ - token_value = uip_current_token_value (); - word = uip_new_word (token_value); - - /* Store details in a word node. */ - uip_parse_match (TOK_WORD); - node = uip_new_node (NODE_WORD); - node->word = word; - break; - } - - case TOK_VARIABLE: - { - const sc_char *token_value; - sc_char *name; - - /* Take a copy of the token's variable name value. */ - token_value = uip_current_token_value (); - name = uip_new_word (token_value); - - /* Store details in a variable node, overloading word. */ - uip_parse_match (TOK_VARIABLE); - node = uip_new_node (NODE_VARIABLE); - node->word = name; - break; - } - - default: - /* Syntax error. */ - sc_error ("uip_parse_element: syntax error," - " unexpected token, %ld\n", (sc_int) uip_parse_lookahead); - longjmp (uip_parse_error, 1); - } - - /* Return the newly created node. */ - assert (node); - return node; +uip_parse_element(void) { + sc_ptnoderef_t node = NULL; + sc_uip_tok_t token; + + /* Handle pattern element based on lookahead token. */ + switch (uip_parse_lookahead) { + case TOK_WHITESPACE: + uip_parse_match(TOK_WHITESPACE); + node = uip_new_node(NODE_WHITESPACE); + break; + + case TOK_CHOICE: + /* Parse a [...[/.../...]] choice. */ + uip_parse_match(TOK_CHOICE); + node = uip_new_node(NODE_CHOICE); + uip_parse_alternatives(node); + uip_parse_match(TOK_CHOICE_END); + break; + + case TOK_OPTIONAL: + /* Parse a {...[/.../...]} optional element. */ + uip_parse_match(TOK_OPTIONAL); + node = uip_new_node(NODE_OPTIONAL); + uip_parse_alternatives(node); + uip_parse_match(TOK_OPTIONAL_END); + break; + + case TOK_WILDCARD: + case TOK_CHARACTER_REFERENCE: + case TOK_OBJECT_REFERENCE: + case TOK_NUMBER_REFERENCE: + case TOK_TEXT_REFERENCE: + /* Parse %mumble% references and * wildcards. */ + token = uip_parse_lookahead; + uip_parse_match(token); + switch (token) { + case TOK_WILDCARD: + node = uip_new_node(NODE_WILDCARD); + break; + case TOK_CHARACTER_REFERENCE: + node = uip_new_node(NODE_CHARACTER_REFERENCE); + break; + case TOK_OBJECT_REFERENCE: + node = uip_new_node(NODE_OBJECT_REFERENCE); + break; + case TOK_NUMBER_REFERENCE: + node = uip_new_node(NODE_NUMBER_REFERENCE); + break; + case TOK_TEXT_REFERENCE: + node = uip_new_node(NODE_TEXT_REFERENCE); + break; + default: + sc_fatal("uip_parse_element: invalid token, %ld\n", (sc_int) token); + } + break; + + case TOK_WORD: { + const sc_char *token_value; + sc_char *word; + + /* Take a copy of the token's word value. */ + token_value = uip_current_token_value(); + word = uip_new_word(token_value); + + /* Store details in a word node. */ + uip_parse_match(TOK_WORD); + node = uip_new_node(NODE_WORD); + node->word = word; + break; + } + + case TOK_VARIABLE: { + const sc_char *token_value; + sc_char *name; + + /* Take a copy of the token's variable name value. */ + token_value = uip_current_token_value(); + name = uip_new_word(token_value); + + /* Store details in a variable node, overloading word. */ + uip_parse_match(TOK_VARIABLE); + node = uip_new_node(NODE_VARIABLE); + node->word = name; + break; + } + + default: + /* Syntax error. */ + sc_error("uip_parse_element: syntax error," + " unexpected token, %ld\n", (sc_int) uip_parse_lookahead); + longjmp(uip_parse_error, 1); + } + + /* Return the newly created node. */ + assert(node); + return node; } @@ -650,63 +605,56 @@ uip_parse_element (void) * Parse a list of pattern elements. */ static void -uip_parse_list (sc_ptnoderef_t list) -{ - sc_ptnoderef_t child, node; - - /* Add elements until a list terminator token is encountered. */ - child = list; - while (TRUE) - { - switch (uip_parse_lookahead) - { - case TOK_CHOICE_END: - case TOK_OPTIONAL_END: - case TOK_ALTERNATES_SEPARATOR: - /* Terminate list building and return. */ - return; - - case TOK_EOS: - /* Place EOS at the appropriate link and return. */ - node = uip_new_node (NODE_EOS); - if (child == list) - child->left_child = node; - else - child->right_sibling = node; - return; - - default: - /* Add the next node at the appropriate link. */ - node = uip_parse_element (); - if (child == list) - { - child->left_child = node; - child = child->left_child; - } - else - { - /* - * Make a special case of a choice or option next to another - * choice or option. In this case, add an (invented) whitespace - * node, to ensure a match with suitable input. - */ - if ((child->type == NODE_OPTIONAL || child->type == NODE_CHOICE) - && (node->type == NODE_OPTIONAL || node->type == NODE_CHOICE)) - { - sc_ptnoderef_t whitespace; - - /* Interpose invented whitespace. */ - whitespace = uip_new_node (NODE_WHITESPACE); - child->right_sibling = whitespace; - child = child->right_sibling; - } - - child->right_sibling = node; - child = child->right_sibling; - } - continue; - } - } +uip_parse_list(sc_ptnoderef_t list) { + sc_ptnoderef_t child, node; + + /* Add elements until a list terminator token is encountered. */ + child = list; + while (TRUE) { + switch (uip_parse_lookahead) { + case TOK_CHOICE_END: + case TOK_OPTIONAL_END: + case TOK_ALTERNATES_SEPARATOR: + /* Terminate list building and return. */ + return; + + case TOK_EOS: + /* Place EOS at the appropriate link and return. */ + node = uip_new_node(NODE_EOS); + if (child == list) + child->left_child = node; + else + child->right_sibling = node; + return; + + default: + /* Add the next node at the appropriate link. */ + node = uip_parse_element(); + if (child == list) { + child->left_child = node; + child = child->left_child; + } else { + /* + * Make a special case of a choice or option next to another + * choice or option. In this case, add an (invented) whitespace + * node, to ensure a match with suitable input. + */ + if ((child->type == NODE_OPTIONAL || child->type == NODE_CHOICE) + && (node->type == NODE_OPTIONAL || node->type == NODE_CHOICE)) { + sc_ptnoderef_t whitespace; + + /* Interpose invented whitespace. */ + whitespace = uip_new_node(NODE_WHITESPACE); + child->right_sibling = whitespace; + child = child->right_sibling; + } + + child->right_sibling = node; + child = child->right_sibling; + } + continue; + } + } } @@ -716,17 +664,15 @@ uip_parse_list (sc_ptnoderef_t list) * Free and destroy a parsed pattern tree. */ static void -uip_destroy_tree (sc_ptnoderef_t node) -{ - if (node) - { - /* Recursively destroy siblings, then left child. */ - uip_destroy_tree (node->right_sibling); - uip_destroy_tree (node->left_child); - - /* Destroy the node itself. */ - uip_destroy_node (node); - } +uip_destroy_tree(sc_ptnoderef_t node) { + if (node) { + /* Recursively destroy siblings, then left child. */ + uip_destroy_tree(node->right_sibling); + uip_destroy_tree(node->left_child); + + /* Destroy the node itself. */ + uip_destroy_node(node); + } } @@ -737,84 +683,78 @@ uip_destroy_tree (sc_ptnoderef_t node) * Print out a pattern match tree. */ static void -uip_debug_dump_node (sc_ptnoderef_t node, sc_int depth) -{ - /* End recursion on null node. */ - if (node) - { - sc_int index_; - - sc_trace (" "); - for (index_ = 0; index_ < depth; index_++) - sc_trace (" "); - - sc_trace ("%p", (void *) node); - switch (node->type) - { - case NODE_CHOICE: - sc_trace (", choice"); - break; - case NODE_OPTIONAL: - sc_trace (", optional"); - break; - case NODE_WILDCARD: - sc_trace (", wildcard"); - break; - case NODE_WHITESPACE: - sc_trace (", whitespace"); - break; - case NODE_CHARACTER_REFERENCE: - sc_trace (", character"); - break; - case NODE_OBJECT_REFERENCE: - sc_trace (", object"); - break; - case NODE_TEXT_REFERENCE: - sc_trace (", text"); - break; - case NODE_NUMBER_REFERENCE: - sc_trace (", number"); - break; - case NODE_WORD: - sc_trace (", word \"%s\"", node->word); - break; - case NODE_VARIABLE: - sc_trace (", variable \"%s\"", node->word); - break; - case NODE_LIST: - sc_trace (", list"); - break; - case NODE_EOS: - sc_trace (", <eos>"); - break; - default: - sc_trace (", unknown type %ld", (sc_int) node->type); - break; - } - if (node->left_child) - sc_trace (", left child %p", (void *) node->left_child); - if (node->right_sibling) - sc_trace (", right sibling %p", (void *) node->right_sibling); - sc_trace ("\n"); - - /* Recursively dump left child, then siblings. */ - uip_debug_dump_node (node->left_child, depth + 1); - uip_debug_dump_node (node->right_sibling, depth); - } +uip_debug_dump_node(sc_ptnoderef_t node, sc_int depth) { + /* End recursion on null node. */ + if (node) { + sc_int index_; + + sc_trace(" "); + for (index_ = 0; index_ < depth; index_++) + sc_trace(" "); + + sc_trace("%p", (void *) node); + switch (node->type) { + case NODE_CHOICE: + sc_trace(", choice"); + break; + case NODE_OPTIONAL: + sc_trace(", optional"); + break; + case NODE_WILDCARD: + sc_trace(", wildcard"); + break; + case NODE_WHITESPACE: + sc_trace(", whitespace"); + break; + case NODE_CHARACTER_REFERENCE: + sc_trace(", character"); + break; + case NODE_OBJECT_REFERENCE: + sc_trace(", object"); + break; + case NODE_TEXT_REFERENCE: + sc_trace(", text"); + break; + case NODE_NUMBER_REFERENCE: + sc_trace(", number"); + break; + case NODE_WORD: + sc_trace(", word \"%s\"", node->word); + break; + case NODE_VARIABLE: + sc_trace(", variable \"%s\"", node->word); + break; + case NODE_LIST: + sc_trace(", list"); + break; + case NODE_EOS: + sc_trace(", <eos>"); + break; + default: + sc_trace(", unknown type %ld", (sc_int) node->type); + break; + } + if (node->left_child) + sc_trace(", left child %p", (void *) node->left_child); + if (node->right_sibling) + sc_trace(", right sibling %p", (void *) node->right_sibling); + sc_trace("\n"); + + /* Recursively dump left child, then siblings. */ + uip_debug_dump_node(node->left_child, depth + 1); + uip_debug_dump_node(node->right_sibling, depth); + } } static void -uip_debug_dump (void) -{ - sc_trace ("UIParser: debug dump follows...\n"); - if (uip_parse_tree) - { - sc_trace ("uip_parse_tree = {\n"); - uip_debug_dump_node (uip_parse_tree, 0); - sc_trace ("}\n"); - } - else - sc_trace ("uip_parse_tree = (nil)\n"); +uip_debug_dump(void) { + sc_trace("UIParser: debug dump follows...\n"); + if (uip_parse_tree) { + sc_trace("uip_parse_tree = {\n"); + uip_debug_dump_node(uip_parse_tree, 0); + sc_trace("}\n"); + } else + sc_trace("uip_parse_tree = (nil)\n"); } @@ -830,23 +770,21 @@ static sc_gameref_t uip_game = NULL; * Set up a string for matching to a pattern tree, and wrap up matching. */ static void -uip_match_start (const sc_char *string, sc_gameref_t game) -{ - /* Save string, and restart index. */ - uip_string = string; - uip_posn = 0; - - /* Save the game we're working on. */ - uip_game = game; +uip_match_start(const sc_char *string, sc_gameref_t game) { + /* Save string, and restart index. */ + uip_string = string; + uip_posn = 0; + + /* Save the game we're working on. */ + uip_game = game; } static void -uip_match_end (void) -{ - /* Clear match target string, and variable set. */ - uip_string = NULL; - uip_posn = 0; - uip_game = NULL; +uip_match_end(void) { + /* Clear match target string, and variable set. */ + uip_string = NULL; + uip_posn = 0; + uip_game = NULL; } @@ -857,15 +795,14 @@ uip_match_end (void) * one. */ static sc_gameref_t -uip_get_game (void) -{ - assert (gs_is_game_valid (uip_game)); - return uip_game; +uip_get_game(void) { + assert(gs_is_game_valid(uip_game)); + return uip_game; } /* Forward declaration of low level node matcher. */ -static sc_bool uip_match_node (sc_ptnoderef_t node); +static sc_bool uip_match_node(sc_ptnoderef_t node); /* * uip_match_eos() @@ -883,298 +820,276 @@ static sc_bool uip_match_node (sc_ptnoderef_t node); * unchanged. */ static sc_bool -uip_match_eos (void) -{ - /* Check that we hit the string's end. */ - return uip_string[uip_posn] == NUL; +uip_match_eos(void) { + /* Check that we hit the string's end. */ + return uip_string[uip_posn] == NUL; } static sc_bool -uip_match_word (sc_ptnoderef_t node) -{ - sc_int length; - const sc_char *word; - - /* Get the word to match. */ - assert (node->word); - word = node->word; - - /* Compare string text with this node's word, ignore case. */ - length = strlen (word); - if (sc_strncasecmp (uip_string + uip_posn, word, length) == 0) - { - /* Word match, advance position and return. */ - uip_posn += length; - return TRUE; - } - - /* No match. */ - return FALSE; +uip_match_word(sc_ptnoderef_t node) { + sc_int length; + const sc_char *word; + + /* Get the word to match. */ + assert(node->word); + word = node->word; + + /* Compare string text with this node's word, ignore case. */ + length = strlen(word); + if (sc_strncasecmp(uip_string + uip_posn, word, length) == 0) { + /* Word match, advance position and return. */ + uip_posn += length; + return TRUE; + } + + /* No match. */ + return FALSE; } static sc_bool -uip_match_variable (sc_ptnoderef_t node) -{ - const sc_gameref_t game = uip_get_game (); - const sc_var_setref_t vars = gs_get_vars (game); - sc_int type; - sc_vartype_t vt_rvalue; - const sc_char *name; - - /* Get the variable name to match, from overloaded word. */ - assert (node->word); - name = node->word; - - /* Get the variable's value. */ - if (var_get (vars, name, &type, &vt_rvalue)) - { - sc_int length; - - /* Compare the value against the current string position. */ - switch (type) - { - case VAR_INTEGER: - { - sc_char value[32]; - - /* Compare numeric against the current string position. */ - sprintf (value, "%ld", vt_rvalue.integer); - length = strlen (value); - if (strncmp (uip_string + uip_posn, value, length) == 0) - { - /* Integer match, advance position and return. */ - uip_posn += length; - return TRUE; - } - break; - } - - case VAR_STRING: - /* Compare string value against the current string position. */ - length = strlen (vt_rvalue.string); - if (sc_strncasecmp (uip_string + uip_posn, - vt_rvalue.string, length) == 0) - { - /* String match, advance position and return. */ - uip_posn += length; - return TRUE; - } - break; - - default: - sc_fatal ("uip_match_variable: invalid variable type, %ld\n", type); - } - } - - /* No match, or no such variable. */ - return FALSE; +uip_match_variable(sc_ptnoderef_t node) { + const sc_gameref_t game = uip_get_game(); + const sc_var_setref_t vars = gs_get_vars(game); + sc_int type; + sc_vartype_t vt_rvalue; + const sc_char *name; + + /* Get the variable name to match, from overloaded word. */ + assert(node->word); + name = node->word; + + /* Get the variable's value. */ + if (var_get(vars, name, &type, &vt_rvalue)) { + sc_int length; + + /* Compare the value against the current string position. */ + switch (type) { + case VAR_INTEGER: { + sc_char value[32]; + + /* Compare numeric against the current string position. */ + sprintf(value, "%ld", vt_rvalue.integer); + length = strlen(value); + if (strncmp(uip_string + uip_posn, value, length) == 0) { + /* Integer match, advance position and return. */ + uip_posn += length; + return TRUE; + } + break; + } + + case VAR_STRING: + /* Compare string value against the current string position. */ + length = strlen(vt_rvalue.string); + if (sc_strncasecmp(uip_string + uip_posn, + vt_rvalue.string, length) == 0) { + /* String match, advance position and return. */ + uip_posn += length; + return TRUE; + } + break; + + default: + sc_fatal("uip_match_variable: invalid variable type, %ld\n", type); + } + } + + /* No match, or no such variable. */ + return FALSE; } static sc_bool -uip_match_whitespace (void) -{ - /* If next character is space, read whitespace and return. */ - if (sc_isspace (uip_string[uip_posn])) - { - /* Space match, advance position and return. */ - while (uip_string[uip_posn] != NUL && sc_isspace (uip_string[uip_posn])) - uip_posn++; - return TRUE; - } - - /* - * No match. However, if we're trying to match space, this is a word - * boundary. So... even though we're not sitting on a space, if the string - * prior character is whitespace, "double-match" the space. - * - * Also, match if we haven't yet matched any text. In effect, this means - * leading spaces on patterns will be ignored. - * - * TODO Is this what we want to happen? It seems harmless, even useful. - */ - if (uip_posn == 0 || sc_isspace (uip_string[uip_posn - 1])) - return TRUE; - - /* - * And that's not all. We also want to match whitespace if we're at the end - * of a string (another word boundary). This will permit patterns that end - * in optional elements to succeed since options and wildcards always match, - * even if to no text. - */ - if (uip_string[uip_posn] == NUL) - return TRUE; - - /* No match. Really. */ - return FALSE; +uip_match_whitespace(void) { + /* If next character is space, read whitespace and return. */ + if (sc_isspace(uip_string[uip_posn])) { + /* Space match, advance position and return. */ + while (uip_string[uip_posn] != NUL && sc_isspace(uip_string[uip_posn])) + uip_posn++; + return TRUE; + } + + /* + * No match. However, if we're trying to match space, this is a word + * boundary. So... even though we're not sitting on a space, if the string + * prior character is whitespace, "double-match" the space. + * + * Also, match if we haven't yet matched any text. In effect, this means + * leading spaces on patterns will be ignored. + * + * TODO Is this what we want to happen? It seems harmless, even useful. + */ + if (uip_posn == 0 || sc_isspace(uip_string[uip_posn - 1])) + return TRUE; + + /* + * And that's not all. We also want to match whitespace if we're at the end + * of a string (another word boundary). This will permit patterns that end + * in optional elements to succeed since options and wildcards always match, + * even if to no text. + */ + if (uip_string[uip_posn] == NUL) + return TRUE; + + /* No match. Really. */ + return FALSE; } static sc_bool -uip_match_list (sc_ptnoderef_t node) -{ - sc_ptnoderef_t child; - - /* - * If this list is empty, fail the match. This special-case handling is - * what catches constructed temporary lists for wildcard-like items that - * don't actually encompass anything. - */ - if (!node->left_child) - return FALSE; - - /* Match everything listed sequentially. */ - for (child = node->left_child; child; child = child->right_sibling) - { - if (!uip_match_node (child)) - { - /* No match. */ - return FALSE; - } - } - - /* Matched. */ - return TRUE; +uip_match_list(sc_ptnoderef_t node) { + sc_ptnoderef_t child; + + /* + * If this list is empty, fail the match. This special-case handling is + * what catches constructed temporary lists for wildcard-like items that + * don't actually encompass anything. + */ + if (!node->left_child) + return FALSE; + + /* Match everything listed sequentially. */ + for (child = node->left_child; child; child = child->right_sibling) { + if (!uip_match_node(child)) { + /* No match. */ + return FALSE; + } + } + + /* Matched. */ + return TRUE; } static sc_bool -uip_match_alternatives (sc_ptnoderef_t node) -{ - sc_ptnoderef_t child; - sc_int start_posn, extent; - sc_bool matched; - - /* Note the start position for rewind between tries. */ - start_posn = uip_posn; - - /* - * Try a match on each of the children, looking to see which one moves the - * position on the furthest. Match on this one. This is a "maximal munch". - */ - extent = uip_posn; - matched = FALSE; - for (child = node->left_child; child; child = child->right_sibling) - { - uip_posn = start_posn; - if (uip_match_node (child)) - { - /* Matched. */ - matched = TRUE; - if (uip_posn > extent) - extent = uip_posn; - } - } - - /* If matched, set position to extent; if not, back to start. */ - uip_posn = matched ? extent : start_posn; - - /* Return match status. */ - return matched; +uip_match_alternatives(sc_ptnoderef_t node) { + sc_ptnoderef_t child; + sc_int start_posn, extent; + sc_bool matched; + + /* Note the start position for rewind between tries. */ + start_posn = uip_posn; + + /* + * Try a match on each of the children, looking to see which one moves the + * position on the furthest. Match on this one. This is a "maximal munch". + */ + extent = uip_posn; + matched = FALSE; + for (child = node->left_child; child; child = child->right_sibling) { + uip_posn = start_posn; + if (uip_match_node(child)) { + /* Matched. */ + matched = TRUE; + if (uip_posn > extent) + extent = uip_posn; + } + } + + /* If matched, set position to extent; if not, back to start. */ + uip_posn = matched ? extent : start_posn; + + /* Return match status. */ + return matched; } static sc_bool -uip_match_choice (sc_ptnoderef_t node) -{ - /* - * Return the result of matching alternatives. The choice will therefore - * fail if none of the alternatives match. - */ - return uip_match_alternatives (node); +uip_match_choice(sc_ptnoderef_t node) { + /* + * Return the result of matching alternatives. The choice will therefore + * fail if none of the alternatives match. + */ + return uip_match_alternatives(node); } static sc_bool -uip_match_optional (sc_ptnoderef_t node) -{ - sc_int start_posn; - sc_ptnoderef_t list; - sc_bool matched; - - /* Note the start position for rewind on empty match. */ - start_posn = uip_posn; - - /* - * Look ahead to see if we can match to nothing, and still have the main - * pattern match. If we can, we'll go with this. It's a "minimal munch"-ish - * strategy, but seems to be what Adrift does in this situation. - */ - list = uip_new_node (NODE_LIST); - list->left_child = node->right_sibling; - - /* Match on the temporary list. */ - matched = uip_match_node (list); - - /* Free the temporary list node. */ - uip_destroy_node (list); - - /* - * If the temporary matched and consumed text, rewind position to match - * nothing. If it didn't, match alternatives to consume anything that may - * match our options. - */ - if (matched && uip_posn > start_posn) - uip_posn = start_posn; - else - uip_match_alternatives (node); - - /* Return TRUE no matter what. */ - return TRUE; +uip_match_optional(sc_ptnoderef_t node) { + sc_int start_posn; + sc_ptnoderef_t list; + sc_bool matched; + + /* Note the start position for rewind on empty match. */ + start_posn = uip_posn; + + /* + * Look ahead to see if we can match to nothing, and still have the main + * pattern match. If we can, we'll go with this. It's a "minimal munch"-ish + * strategy, but seems to be what Adrift does in this situation. + */ + list = uip_new_node(NODE_LIST); + list->left_child = node->right_sibling; + + /* Match on the temporary list. */ + matched = uip_match_node(list); + + /* Free the temporary list node. */ + uip_destroy_node(list); + + /* + * If the temporary matched and consumed text, rewind position to match + * nothing. If it didn't, match alternatives to consume anything that may + * match our options. + */ + if (matched && uip_posn > start_posn) + uip_posn = start_posn; + else + uip_match_alternatives(node); + + /* Return TRUE no matter what. */ + return TRUE; } static sc_bool -uip_match_wildcard (sc_ptnoderef_t node) -{ - sc_int start_posn, limit, index_; - sc_bool matched; - sc_ptnoderef_t list; - - /* - * At least one game uses patterns like "thing******...". Why? Who knows. - * But if we're in a list of wildcards, and not the first, ignore the call; - * only the final one needs handling. - */ - if (node->right_sibling && node->right_sibling->type == NODE_WILDCARD) - return TRUE; - - /* Note the start position for rewind on no match. */ - start_posn = uip_posn; - - /* - * To make life a little easier, we'll match on the tree to the right of - * this node by constructing a temporary list node, containing stuff to the - * right of the wildcard, and then matching on that. - */ - list = uip_new_node (NODE_LIST); - list->left_child = node->right_sibling; - - /* - * Repeatedly try to match the rest of the tree at successive character - * positions, and stop if we succeed. This is a "minimal munch", which may - * or may not be the right thing to be doing here. - * - * When scanning forward, take care to include the NUL, needed to match - * TOK_EOS. - */ - matched = FALSE; - limit = strlen (uip_string) + 1; - for (index_ = uip_posn + 1; index_ < limit; index_++) - { - uip_posn = index_; - if (uip_match_node (list)) - { - /* Wildcard match at this point. */ - uip_posn = index_; - matched = TRUE; - break; - } - } - - /* Free the temporary list node. */ - uip_destroy_node (list); - - /* If we didn't match in the loop, restore position. */ - if (!matched) - uip_posn = start_posn; - - /* Return TRUE whether we matched text or not. */ - return TRUE; +uip_match_wildcard(sc_ptnoderef_t node) { + sc_int start_posn, limit, index_; + sc_bool matched; + sc_ptnoderef_t list; + + /* + * At least one game uses patterns like "thing******...". Why? Who knows. + * But if we're in a list of wildcards, and not the first, ignore the call; + * only the final one needs handling. + */ + if (node->right_sibling && node->right_sibling->type == NODE_WILDCARD) + return TRUE; + + /* Note the start position for rewind on no match. */ + start_posn = uip_posn; + + /* + * To make life a little easier, we'll match on the tree to the right of + * this node by constructing a temporary list node, containing stuff to the + * right of the wildcard, and then matching on that. + */ + list = uip_new_node(NODE_LIST); + list->left_child = node->right_sibling; + + /* + * Repeatedly try to match the rest of the tree at successive character + * positions, and stop if we succeed. This is a "minimal munch", which may + * or may not be the right thing to be doing here. + * + * When scanning forward, take care to include the NUL, needed to match + * TOK_EOS. + */ + matched = FALSE; + limit = strlen(uip_string) + 1; + for (index_ = uip_posn + 1; index_ < limit; index_++) { + uip_posn = index_; + if (uip_match_node(list)) { + /* Wildcard match at this point. */ + uip_posn = index_; + matched = TRUE; + break; + } + } + + /* Free the temporary list node. */ + uip_destroy_node(list); + + /* If we didn't match in the loop, restore position. */ + if (!matched) + uip_posn = start_posn; + + /* Return TRUE whether we matched text or not. */ + return TRUE; } @@ -1185,100 +1100,92 @@ uip_match_wildcard (sc_ptnoderef_t node) * Attempt to match a number, or a word, from the string. */ static sc_bool -uip_match_number (void) -{ - const sc_gameref_t game = uip_get_game (); - const sc_var_setref_t vars = gs_get_vars (game); - sc_int number; - - /* Attempt to read a number from input. */ - if (sscanf (uip_string + uip_posn, "%ld", &number) == 1) - { - /* Advance position over the number. */ - while (uip_string[uip_posn] == MINUS || uip_string[uip_posn] == PLUS) - uip_posn++; - while (sc_isdigit (uip_string[uip_posn])) - uip_posn++; - - /* Set number reference in variables and return. */ - var_set_ref_number (vars, number); - return TRUE; - } - - /* No match. */ - return FALSE; +uip_match_number(void) { + const sc_gameref_t game = uip_get_game(); + const sc_var_setref_t vars = gs_get_vars(game); + sc_int number; + + /* Attempt to read a number from input. */ + if (sscanf(uip_string + uip_posn, "%ld", &number) == 1) { + /* Advance position over the number. */ + while (uip_string[uip_posn] == MINUS || uip_string[uip_posn] == PLUS) + uip_posn++; + while (sc_isdigit(uip_string[uip_posn])) + uip_posn++; + + /* Set number reference in variables and return. */ + var_set_ref_number(vars, number); + return TRUE; + } + + /* No match. */ + return FALSE; } static sc_bool -uip_match_text (sc_ptnoderef_t node) -{ - const sc_gameref_t game = uip_get_game (); - const sc_var_setref_t vars = gs_get_vars (game); - sc_int start_posn, limit, index_; - sc_bool matched; - sc_ptnoderef_t list; - - /* Note the start position for rewind on no match. */ - start_posn = uip_posn; - - /* - * As with wildcards, create a temporary list of the stuff to the right of - * the reference node, and match on that. - */ - list = uip_new_node (NODE_LIST); - list->left_child = node->right_sibling; - - /* - * Again, as with wildcards, repeatedly try to match the rest of the tree at - * successive character positions, stopping if we succeed. - */ - matched = FALSE; - limit = strlen (uip_string) + 1; - for (index_ = uip_posn + 1; index_ < limit; index_++) - { - uip_posn = index_; - if (uip_match_node (list)) - { - /* Text reference match at this point. */ - uip_posn = index_; - matched = TRUE; - break; - } - } - - /* Free the temporary list node. */ - uip_destroy_node (list); - - /* See if we found a match in the loop. */ - if (matched) - { - sc_char *string; - - /* Found a match; create a string and save the text. */ - string = (sc_char *)sc_malloc (uip_posn - start_posn + 1); - memcpy (string, uip_string + start_posn, uip_posn - start_posn); - string[uip_posn - start_posn] = NUL; - - /* - * Adrift seems to save referenced text as all-lowercase; we need to do - * the same. - */ - for (index_ = 0; string[index_] != NUL; index_++) - string[index_] = sc_tolower (string[index_]); - var_set_ref_text (vars, string); - sc_free (string); - - /* Return TRUE since we matched text. */ - return TRUE; - } - else - { - /* We didn't match in the loop; restore position. */ - uip_posn = start_posn; - - /* Return FALSE on no match. */ - return FALSE; - } +uip_match_text(sc_ptnoderef_t node) { + const sc_gameref_t game = uip_get_game(); + const sc_var_setref_t vars = gs_get_vars(game); + sc_int start_posn, limit, index_; + sc_bool matched; + sc_ptnoderef_t list; + + /* Note the start position for rewind on no match. */ + start_posn = uip_posn; + + /* + * As with wildcards, create a temporary list of the stuff to the right of + * the reference node, and match on that. + */ + list = uip_new_node(NODE_LIST); + list->left_child = node->right_sibling; + + /* + * Again, as with wildcards, repeatedly try to match the rest of the tree at + * successive character positions, stopping if we succeed. + */ + matched = FALSE; + limit = strlen(uip_string) + 1; + for (index_ = uip_posn + 1; index_ < limit; index_++) { + uip_posn = index_; + if (uip_match_node(list)) { + /* Text reference match at this point. */ + uip_posn = index_; + matched = TRUE; + break; + } + } + + /* Free the temporary list node. */ + uip_destroy_node(list); + + /* See if we found a match in the loop. */ + if (matched) { + sc_char *string; + + /* Found a match; create a string and save the text. */ + string = (sc_char *)sc_malloc(uip_posn - start_posn + 1); + memcpy(string, uip_string + start_posn, uip_posn - start_posn); + string[uip_posn - start_posn] = NUL; + + /* + * Adrift seems to save referenced text as all-lowercase; we need to do + * the same. + */ + for (index_ = 0; string[index_] != NUL; index_++) + string[index_] = sc_tolower(string[index_]); + var_set_ref_text(vars, string); + sc_free(string); + + /* Return TRUE since we matched text. */ + return TRUE; + } else { + /* We didn't match in the loop; restore position. */ + uip_posn = start_posn; + + /* Return FALSE on no match. */ + return FALSE; + } } @@ -1289,25 +1196,24 @@ uip_match_text (sc_ptnoderef_t node) * %character% and %object% matchers. Returns the revised string position. */ static sc_int -uip_skip_article (const sc_char *string, sc_int start) -{ - sc_int posn; - - /* Skip over articles. */ - posn = start; - if (sc_compare_word (string + posn, "a", 1)) - posn += 1; - else if (sc_compare_word (string + posn, "an", 2)) - posn += 2; - else if (sc_compare_word (string + posn, "the", 3)) - posn += 3; - else if (sc_compare_word (string + posn, "some", 4)) - posn += 4; - - /* Skip any whitespace, and return. */ - while (sc_isspace (string[posn]) && string[posn] != NUL) - posn++; - return posn; +uip_skip_article(const sc_char *string, sc_int start) { + sc_int posn; + + /* Skip over articles. */ + posn = start; + if (sc_compare_word(string + posn, "a", 1)) + posn += 1; + else if (sc_compare_word(string + posn, "an", 2)) + posn += 2; + else if (sc_compare_word(string + posn, "the", 3)) + posn += 3; + else if (sc_compare_word(string + posn, "some", 4)) + posn += 4; + + /* Skip any whitespace, and return. */ + while (sc_isspace(string[posn]) && string[posn] != NUL) + posn++; + return posn; } @@ -1320,51 +1226,49 @@ uip_skip_article (const sc_char *string, sc_int start) * the words passed in (the new value of uip_posn on match). */ static sc_int -uip_compare_reference (const sc_char *words) -{ - sc_int wpos, posn; - - /* Skip articles and lead in space on words and string. */ - wpos = uip_skip_article (words, 0); - posn = uip_skip_article (uip_string, uip_posn); - - /* Match characters from words with the string at position. */ - while (TRUE) - { - /* Any character mismatch means no words match. */ - if (sc_tolower (words[wpos]) != sc_tolower (uip_string[posn])) - return 0; - - /* Move to next character in each. */ - wpos++; - posn++; - - /* - * If at space, advance over whitespace in words list. Stop when we - * hit the end of the words list. - */ - while (sc_isspace (words[wpos]) && words[wpos] != NUL) - wpos++; - if (words[wpos] == NUL) - break; - - /* - * About to match another word, so advance over whitespace in the - * current string too. - */ - while (sc_isspace (uip_string[posn]) && uip_string[posn] != NUL) - posn++; - } - - /* - * We reached the end of words. If we're at the end of the match string, or - * at spaces, we've matched. - */ - if (sc_isspace (uip_string[posn]) || uip_string[posn] == NUL) - return posn; - - /* More text after the match, so it's not quite a match. */ - return 0; +uip_compare_reference(const sc_char *words) { + sc_int wpos, posn; + + /* Skip articles and lead in space on words and string. */ + wpos = uip_skip_article(words, 0); + posn = uip_skip_article(uip_string, uip_posn); + + /* Match characters from words with the string at position. */ + while (TRUE) { + /* Any character mismatch means no words match. */ + if (sc_tolower(words[wpos]) != sc_tolower(uip_string[posn])) + return 0; + + /* Move to next character in each. */ + wpos++; + posn++; + + /* + * If at space, advance over whitespace in words list. Stop when we + * hit the end of the words list. + */ + while (sc_isspace(words[wpos]) && words[wpos] != NUL) + wpos++; + if (words[wpos] == NUL) + break; + + /* + * About to match another word, so advance over whitespace in the + * current string too. + */ + while (sc_isspace(uip_string[posn]) && uip_string[posn] != NUL) + posn++; + } + + /* + * We reached the end of words. If we're at the end of the match string, or + * at spaces, we've matched. + */ + if (sc_isspace(uip_string[posn]) || uip_string[posn] == NUL) + return posn; + + /* More text after the match, so it's not quite a match. */ + return 0; } @@ -1376,28 +1280,27 @@ uip_compare_reference (const sc_char *words) * Returns the extent of the match, or zero if no match. */ static sc_int -uip_compare_prefixed_name (const sc_char *prefix, const sc_char *name) -{ - sc_char buffer[UIP_SHORT_WORD_SIZE + UIP_SHORT_WORD_SIZE + 1]; - sc_char *string; - sc_int required, extent; - - /* Create a prefixed string, using the local buffer if possible. */ - required = strlen (prefix) + strlen (name) + 2; - string = required > (sc_int) sizeof (buffer) ? (sc_char *)sc_malloc (required) : buffer; - sprintf (string, "%s %s", prefix, name); - - /* Check against the prefixed name first, free string if required. */ - extent = uip_compare_reference (string); - if (string != buffer) - sc_free (string); - - /* If no match there, retry with just the plain name. */ - if (extent == 0) - extent = uip_compare_reference (name); - - /* Return the count of characters consumed in matching. */ - return extent; +uip_compare_prefixed_name(const sc_char *prefix, const sc_char *name) { + sc_char buffer[UIP_SHORT_WORD_SIZE + UIP_SHORT_WORD_SIZE + 1]; + sc_char *string; + sc_int required, extent; + + /* Create a prefixed string, using the local buffer if possible. */ + required = strlen(prefix) + strlen(name) + 2; + string = required > (sc_int) sizeof(buffer) ? (sc_char *)sc_malloc(required) : buffer; + sprintf(string, "%s %s", prefix, name); + + /* Check against the prefixed name first, free string if required. */ + extent = uip_compare_reference(string); + if (string != buffer) + sc_free(string); + + /* If no match there, retry with just the plain name. */ + if (extent == 0) + extent = uip_compare_reference(name); + + /* Return the count of characters consumed in matching. */ + return extent; } @@ -1409,32 +1312,31 @@ uip_compare_prefixed_name (const sc_char *prefix, const sc_char *name) * "table". */ static sc_bool -uip_match_remainder (sc_ptnoderef_t node, sc_int extent) -{ - sc_ptnoderef_t list; - sc_int start_posn; - sc_bool matched; - - /* Note the start position, then advance to the given extent. */ - start_posn = uip_posn; - uip_posn = extent; - - /* - * Try to match everything after the node passed in, at this position in the - * string. - */ - list = uip_new_node (NODE_LIST); - list->left_child = node->right_sibling; - - /* Match on the temporary list. */ - matched = uip_match_node (list); - - /* Free the temporary list node, and restore position. */ - uip_destroy_node (list); - uip_posn = start_posn; - - /* Return TRUE if the pattern remainder matched. */ - return matched; +uip_match_remainder(sc_ptnoderef_t node, sc_int extent) { + sc_ptnoderef_t list; + sc_int start_posn; + sc_bool matched; + + /* Note the start position, then advance to the given extent. */ + start_posn = uip_posn; + uip_posn = extent; + + /* + * Try to match everything after the node passed in, at this position in the + * string. + */ + list = uip_new_node(NODE_LIST); + list->left_child = node->right_sibling; + + /* Match on the temporary list. */ + matched = uip_match_node(list); + + /* Free the temporary list node, and restore position. */ + uip_destroy_node(list); + uip_posn = start_posn; + + /* Return TRUE if the pattern remainder matched. */ + return matched; } @@ -1446,100 +1348,94 @@ uip_match_remainder (sc_ptnoderef_t node, sc_int extent) * for any that match. The final one to match is also stored in variables. */ static sc_bool -uip_match_character (sc_ptnoderef_t node) -{ - const sc_gameref_t game = uip_get_game (); - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - sc_int npc_count, npc, max_extent; - - if (uip_trace) - sc_trace ("UIParser: attempting to match %%character%%\n"); - - /* Clear all current character references. */ - gs_clear_npc_references (game); - - /* Iterate characters, looking for a name or alias match. */ - max_extent = 0; - npc_count = gs_npc_count (game); - for (npc = 0; npc < npc_count; npc++) - { - sc_vartype_t vt_key[4]; - const sc_char *prefix, *name; - sc_int alias_count, alias, extent; - - /* Get the NPC's prefix and name. */ - vt_key[0].string = "NPCs"; - vt_key[1].integer = npc; - vt_key[2].string = "Prefix"; - prefix = prop_get_string (bundle, "S<-sis", vt_key); - vt_key[2].string = "Name"; - name = prop_get_string (bundle, "S<-sis", vt_key); - - if (uip_trace) - sc_trace ("UIParser: trying %s\n", name); - - /* Compare this name, both prefixed and not. */ - extent = uip_compare_prefixed_name (prefix, name); - if (extent > 0 && uip_match_remainder (node, extent)) - { - if (uip_trace) - sc_trace ("UIParser: matched\n"); - - /* Increase the maximum match extent if required. */ - max_extent = (extent > max_extent) ? extent : max_extent; - - /* Save match in variables and game. */ - var_set_ref_character (vars, npc); - game->npc_references[npc] = TRUE; - } - - /* Now compare against all NPC aliases. */ - vt_key[2].string = "Alias"; - alias_count = prop_get_child_count (bundle, "I<-sis", vt_key); - - for (alias = 0; alias < alias_count; alias++) - { - const sc_char *alias_name; - - /* - * Get the NPC alias. Version 3.9 games introduce empty aliases, - * so check here. - */ - vt_key[3].integer = alias; - alias_name = prop_get_string (bundle, "S<-sisi", vt_key); - if (sc_strempty (alias_name)) - continue; - - if (uip_trace) - sc_trace ("UIParser: trying alias %s\n", alias_name); - - /* Compare this alias name, both prefixed and not. */ - extent = uip_compare_prefixed_name (prefix, alias_name); - if (extent > 0 && uip_match_remainder (node, extent)) - { - if (uip_trace) - sc_trace ("UIParser: matched\n"); - - /* Increase the maximum match extent if required. */ - max_extent = (extent > max_extent) ? extent : max_extent; - - /* Save match in variables and game. */ - var_set_ref_character (vars, npc); - game->npc_references[npc] = TRUE; - } - } - } - - /* On match, advance position and return successfully. */ - if (max_extent > 0) - { - uip_posn = max_extent; - return TRUE; - } - - /* No match. */ - return FALSE; +uip_match_character(sc_ptnoderef_t node) { + const sc_gameref_t game = uip_get_game(); + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + sc_int npc_count, npc, max_extent; + + if (uip_trace) + sc_trace("UIParser: attempting to match %%character%%\n"); + + /* Clear all current character references. */ + gs_clear_npc_references(game); + + /* Iterate characters, looking for a name or alias match. */ + max_extent = 0; + npc_count = gs_npc_count(game); + for (npc = 0; npc < npc_count; npc++) { + sc_vartype_t vt_key[4]; + const sc_char *prefix, *name; + sc_int alias_count, alias, extent; + + /* Get the NPC's prefix and name. */ + vt_key[0].string = "NPCs"; + vt_key[1].integer = npc; + vt_key[2].string = "Prefix"; + prefix = prop_get_string(bundle, "S<-sis", vt_key); + vt_key[2].string = "Name"; + name = prop_get_string(bundle, "S<-sis", vt_key); + + if (uip_trace) + sc_trace("UIParser: trying %s\n", name); + + /* Compare this name, both prefixed and not. */ + extent = uip_compare_prefixed_name(prefix, name); + if (extent > 0 && uip_match_remainder(node, extent)) { + if (uip_trace) + sc_trace("UIParser: matched\n"); + + /* Increase the maximum match extent if required. */ + max_extent = (extent > max_extent) ? extent : max_extent; + + /* Save match in variables and game. */ + var_set_ref_character(vars, npc); + game->npc_references[npc] = TRUE; + } + + /* Now compare against all NPC aliases. */ + vt_key[2].string = "Alias"; + alias_count = prop_get_child_count(bundle, "I<-sis", vt_key); + + for (alias = 0; alias < alias_count; alias++) { + const sc_char *alias_name; + + /* + * Get the NPC alias. Version 3.9 games introduce empty aliases, + * so check here. + */ + vt_key[3].integer = alias; + alias_name = prop_get_string(bundle, "S<-sisi", vt_key); + if (sc_strempty(alias_name)) + continue; + + if (uip_trace) + sc_trace("UIParser: trying alias %s\n", alias_name); + + /* Compare this alias name, both prefixed and not. */ + extent = uip_compare_prefixed_name(prefix, alias_name); + if (extent > 0 && uip_match_remainder(node, extent)) { + if (uip_trace) + sc_trace("UIParser: matched\n"); + + /* Increase the maximum match extent if required. */ + max_extent = (extent > max_extent) ? extent : max_extent; + + /* Save match in variables and game. */ + var_set_ref_character(vars, npc); + game->npc_references[npc] = TRUE; + } + } + } + + /* On match, advance position and return successfully. */ + if (max_extent > 0) { + uip_posn = max_extent; + return TRUE; + } + + /* No match. */ + return FALSE; } @@ -1551,100 +1447,94 @@ uip_match_character (sc_ptnoderef_t node) * for any that match. The final one to match is also stored in variables. */ static sc_bool -uip_match_object (sc_ptnoderef_t node) -{ - const sc_gameref_t game = uip_get_game (); - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - sc_int object_count, object, max_extent; - - if (uip_trace) - sc_trace ("UIParser: attempting to match %%object%%\n"); - - /* Clear all current object references. */ - gs_clear_object_references (game); - - /* Iterate objects, looking for a name or alias match. */ - max_extent = 0; - object_count = gs_object_count (game); - for (object = 0; object < object_count; object++) - { - sc_vartype_t vt_key[4]; - const sc_char *prefix, *name; - sc_int alias_count, alias, extent; - - /* Get the object's prefix and name. */ - vt_key[0].string = "Objects"; - vt_key[1].integer = object; - vt_key[2].string = "Prefix"; - prefix = prop_get_string (bundle, "S<-sis", vt_key); - vt_key[2].string = "Short"; - name = prop_get_string (bundle, "S<-sis", vt_key); - - if (uip_trace) - sc_trace ("UIParser: trying %s\n", name); - - /* Compare this name, both prefixed and not. */ - extent = uip_compare_prefixed_name (prefix, name); - if (extent > 0 && uip_match_remainder (node, extent)) - { - if (uip_trace) - sc_trace ("UIParser: matched\n"); - - /* Increase the maximum match extent if required. */ - max_extent = (extent > max_extent) ? extent : max_extent; - - /* Save match in variables and game. */ - var_set_ref_object (vars, object); - game->object_references[object] = TRUE; - } - - /* Now compare against all object aliases. */ - vt_key[2].string = "Alias"; - alias_count = prop_get_child_count (bundle, "I<-sis", vt_key); - - for (alias = 0; alias < alias_count; alias++) - { - const sc_char *alias_name; - - /* - * Get the object alias. Version 3.9 games introduce empty aliases, - * so check here. - */ - vt_key[3].integer = alias; - alias_name = prop_get_string (bundle, "S<-sisi", vt_key); - if (sc_strempty (alias_name)) - continue; - - if (uip_trace) - sc_trace ("UIParser: trying alias %s\n", alias_name); - - /* Compare this alias name, both prefixed and not. */ - extent = uip_compare_prefixed_name (prefix, alias_name); - if (extent > 0 && uip_match_remainder (node, extent)) - { - if (uip_trace) - sc_trace ("UIParser: matched\n"); - - /* Increase the maximum match extent if required. */ - max_extent = (extent > max_extent) ? extent : max_extent; - - /* Save match in variables and game. */ - var_set_ref_object (vars, object); - game->object_references[object] = TRUE; - } - } - } - - /* On match, advance position and return successfully. */ - if (max_extent > 0) - { - uip_posn = max_extent; - return TRUE; - } - - /* No match. */ - return FALSE; +uip_match_object(sc_ptnoderef_t node) { + const sc_gameref_t game = uip_get_game(); + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + sc_int object_count, object, max_extent; + + if (uip_trace) + sc_trace("UIParser: attempting to match %%object%%\n"); + + /* Clear all current object references. */ + gs_clear_object_references(game); + + /* Iterate objects, looking for a name or alias match. */ + max_extent = 0; + object_count = gs_object_count(game); + for (object = 0; object < object_count; object++) { + sc_vartype_t vt_key[4]; + const sc_char *prefix, *name; + sc_int alias_count, alias, extent; + + /* Get the object's prefix and name. */ + vt_key[0].string = "Objects"; + vt_key[1].integer = object; + vt_key[2].string = "Prefix"; + prefix = prop_get_string(bundle, "S<-sis", vt_key); + vt_key[2].string = "Short"; + name = prop_get_string(bundle, "S<-sis", vt_key); + + if (uip_trace) + sc_trace("UIParser: trying %s\n", name); + + /* Compare this name, both prefixed and not. */ + extent = uip_compare_prefixed_name(prefix, name); + if (extent > 0 && uip_match_remainder(node, extent)) { + if (uip_trace) + sc_trace("UIParser: matched\n"); + + /* Increase the maximum match extent if required. */ + max_extent = (extent > max_extent) ? extent : max_extent; + + /* Save match in variables and game. */ + var_set_ref_object(vars, object); + game->object_references[object] = TRUE; + } + + /* Now compare against all object aliases. */ + vt_key[2].string = "Alias"; + alias_count = prop_get_child_count(bundle, "I<-sis", vt_key); + + for (alias = 0; alias < alias_count; alias++) { + const sc_char *alias_name; + + /* + * Get the object alias. Version 3.9 games introduce empty aliases, + * so check here. + */ + vt_key[3].integer = alias; + alias_name = prop_get_string(bundle, "S<-sisi", vt_key); + if (sc_strempty(alias_name)) + continue; + + if (uip_trace) + sc_trace("UIParser: trying alias %s\n", alias_name); + + /* Compare this alias name, both prefixed and not. */ + extent = uip_compare_prefixed_name(prefix, alias_name); + if (extent > 0 && uip_match_remainder(node, extent)) { + if (uip_trace) + sc_trace("UIParser: matched\n"); + + /* Increase the maximum match extent if required. */ + max_extent = (extent > max_extent) ? extent : max_extent; + + /* Save match in variables and game. */ + var_set_ref_object(vars, object); + game->object_references[object] = TRUE; + } + } + } + + /* On match, advance position and return successfully. */ + if (max_extent > 0) { + uip_posn = max_extent; + return TRUE; + } + + /* No match. */ + return FALSE; } @@ -1656,54 +1546,52 @@ uip_match_object (sc_ptnoderef_t node) * position unchanged. */ static sc_bool -uip_match_node (sc_ptnoderef_t node) -{ - sc_bool match = FALSE; - - /* Match depending on node type. */ - switch (node->type) - { - case NODE_EOS: - match = uip_match_eos (); - break; - case NODE_WORD: - match = uip_match_word (node); - break; - case NODE_VARIABLE: - match = uip_match_variable (node); - break; - case NODE_WHITESPACE: - match = uip_match_whitespace (); - break; - case NODE_LIST: - match = uip_match_list (node); - break; - case NODE_CHOICE: - match = uip_match_choice (node); - break; - case NODE_OPTIONAL: - match = uip_match_optional (node); - break; - case NODE_WILDCARD: - match = uip_match_wildcard (node); - break; - case NODE_CHARACTER_REFERENCE: - match = uip_match_character (node); - break; - case NODE_OBJECT_REFERENCE: - match = uip_match_object (node); - break; - case NODE_NUMBER_REFERENCE: - match = uip_match_number (); - break; - case NODE_TEXT_REFERENCE: - match = uip_match_text (node); - break; - default: - sc_fatal ("uip_match_node: invalid type, %ld\n", (sc_int) node->type); - } - - return match; +uip_match_node(sc_ptnoderef_t node) { + sc_bool match = FALSE; + + /* Match depending on node type. */ + switch (node->type) { + case NODE_EOS: + match = uip_match_eos(); + break; + case NODE_WORD: + match = uip_match_word(node); + break; + case NODE_VARIABLE: + match = uip_match_variable(node); + break; + case NODE_WHITESPACE: + match = uip_match_whitespace(); + break; + case NODE_LIST: + match = uip_match_list(node); + break; + case NODE_CHOICE: + match = uip_match_choice(node); + break; + case NODE_OPTIONAL: + match = uip_match_optional(node); + break; + case NODE_WILDCARD: + match = uip_match_wildcard(node); + break; + case NODE_CHARACTER_REFERENCE: + match = uip_match_character(node); + break; + case NODE_OBJECT_REFERENCE: + match = uip_match_object(node); + break; + case NODE_NUMBER_REFERENCE: + match = uip_match_number(); + break; + case NODE_TEXT_REFERENCE: + match = uip_match_text(node); + break; + default: + sc_fatal("uip_match_node: invalid type, %ld\n", (sc_int) node->type); + } + + return match; } @@ -1718,33 +1606,31 @@ uip_match_node (sc_ptnoderef_t node) * buffer passed in), or call uip_free_cleansed_string. */ static sc_char * -uip_cleanse_string (const sc_char *original, sc_char *buffer, sc_int length) -{ - sc_int required; - sc_char *string; - - /* - * Use the supplied buffer if it is long enough, otherwise allocate, and - * copy the string. - */ - required = strlen (original) + 1; - string = (required < length) ? buffer : (sc_char *)sc_malloc (required); - strcpy (string, original); - - /* Trim, and return the string. */ - sc_trim_string (string); - return string; +uip_cleanse_string(const sc_char *original, sc_char *buffer, sc_int length) { + sc_int required; + sc_char *string; + + /* + * Use the supplied buffer if it is long enough, otherwise allocate, and + * copy the string. + */ + required = strlen(original) + 1; + string = (required < length) ? buffer : (sc_char *)sc_malloc(required); + strcpy(string, original); + + /* Trim, and return the string. */ + sc_trim_string(string); + return string; } static sc_char * -uip_free_cleansed_string (sc_char *string, const sc_char *buffer) -{ - /* Free if the string was allocated by the function above. */ - if (string != buffer) - sc_free (string); - - /* Always returns NULL, for the syntactic convenience of the caller. */ - return NULL; +uip_free_cleansed_string(sc_char *string, const sc_char *buffer) { + /* Free if the string was allocated by the function above. */ + if (string != buffer) + sc_free(string); + + /* Always returns NULL, for the syntactic convenience of the caller. */ + return NULL; } @@ -1754,9 +1640,8 @@ uip_free_cleansed_string (sc_char *string, const sc_char *buffer) * Set pattern match tracing on/off. */ void -uip_debug_trace (sc_bool flag) -{ - uip_trace = flag; +uip_debug_trace(sc_bool flag) { + uip_trace = flag; } @@ -1768,60 +1653,56 @@ uip_debug_trace (sc_bool flag) * need to copy each of the pattern and match strings passed in. */ sc_bool -uip_match (const sc_char *pattern, const sc_char *string, sc_gameref_t game) -{ - static sc_char *cleansed; /* For setjmp safety. */ - sc_char buffer[UIP_ALLOCATION_AVOIDANCE_SIZE]; - sc_bool match; - assert (pattern && string && game); - - /* Start tokenizer. */ - cleansed = uip_cleanse_string (pattern, buffer, sizeof (buffer)); - if (uip_trace) - sc_trace ("UIParser: pattern \"%s\"\n", cleansed); - uip_tokenize_start (cleansed); - - /* Try parsing the pattern, and catch errors. */ - if (setjmp (uip_parse_error) == 0) - { - /* Parse the pattern into a match tree. */ - uip_parse_lookahead = uip_next_token (); - uip_parse_tree = uip_new_node (NODE_LIST); - uip_parse_list (uip_parse_tree); - uip_tokenize_end (); - cleansed = uip_free_cleansed_string (cleansed, buffer); - } - else - { - /* Parse error -- clean up and fail. */ - uip_tokenize_end (); - uip_destroy_tree (uip_parse_tree); - uip_parse_tree = NULL; - cleansed = uip_free_cleansed_string (cleansed, buffer); - return FALSE; - } - - /* Dump out the pattern tree if requested. */ - if (if_get_trace_flag (SC_DUMP_PARSER_TREES)) - uip_debug_dump (); - - /* Match the string to the pattern tree. */ - cleansed = uip_cleanse_string (string, buffer, sizeof (buffer)); - if (uip_trace) - sc_trace ("UIParser: string \"%s\"\n", cleansed); - uip_match_start (cleansed, game); - match = uip_match_node (uip_parse_tree); - - /* Clean up matching and free the parsed pattern tree. */ - uip_match_end (); - cleansed = uip_free_cleansed_string (cleansed, buffer); - uip_destroy_tree (uip_parse_tree); - uip_parse_tree = NULL; - - /* Return result of matching. */ - if (uip_trace) - sc_trace ("UIParser: %s\n", match ? "MATCHED!" : "No match"); - return match; +uip_match(const sc_char *pattern, const sc_char *string, sc_gameref_t game) { + static sc_char *cleansed; /* For setjmp safety. */ + sc_char buffer[UIP_ALLOCATION_AVOIDANCE_SIZE]; + sc_bool match; + assert(pattern && string && game); + + /* Start tokenizer. */ + cleansed = uip_cleanse_string(pattern, buffer, sizeof(buffer)); + if (uip_trace) + sc_trace("UIParser: pattern \"%s\"\n", cleansed); + uip_tokenize_start(cleansed); + + /* Try parsing the pattern, and catch errors. */ + if (setjmp(uip_parse_error) == 0) { + /* Parse the pattern into a match tree. */ + uip_parse_lookahead = uip_next_token(); + uip_parse_tree = uip_new_node(NODE_LIST); + uip_parse_list(uip_parse_tree); + uip_tokenize_end(); + cleansed = uip_free_cleansed_string(cleansed, buffer); + } else { + /* Parse error -- clean up and fail. */ + uip_tokenize_end(); + uip_destroy_tree(uip_parse_tree); + uip_parse_tree = NULL; + cleansed = uip_free_cleansed_string(cleansed, buffer); + return FALSE; + } + + /* Dump out the pattern tree if requested. */ + if (if_get_trace_flag(SC_DUMP_PARSER_TREES)) + uip_debug_dump(); + + /* Match the string to the pattern tree. */ + cleansed = uip_cleanse_string(string, buffer, sizeof(buffer)); + if (uip_trace) + sc_trace("UIParser: string \"%s\"\n", cleansed); + uip_match_start(cleansed, game); + match = uip_match_node(uip_parse_tree); + + /* Clean up matching and free the parsed pattern tree. */ + uip_match_end(); + cleansed = uip_free_cleansed_string(cleansed, buffer); + uip_destroy_tree(uip_parse_tree); + uip_parse_tree = NULL; + + /* Return result of matching. */ + if (uip_trace) + sc_trace("UIParser: %s\n", match ? "MATCHED!" : "No match"); + return match; } @@ -1833,157 +1714,137 @@ uip_match (const sc_char *pattern, const sc_char *string, sc_gameref_t game) * return string is malloc'ed, so the caller needs to remember to free it. */ sc_char * -uip_replace_pronouns (sc_gameref_t game, const sc_char *string) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - sc_int buffer_allocation; - sc_char *buffer; - const sc_char *current; - assert (string); - - if (uip_trace) - sc_trace ("UIParser: pronoun search \"%s\"\n", string); - - /* Begin with a NULL buffer for lazy allocation. */ - buffer_allocation = 0; - buffer = NULL; - - /* Search for pronouns until no more string remains. */ - current = string + strspn (string, WHITESPACE); - while (current[0] != NUL) - { - sc_vartype_t vt_key[3]; - sc_int object, npc, extent; - const sc_char *prefix, *name; - - /* Initially, no object or NPC, no names, and a zero extent. */ - object = npc = -1; - prefix = name = NULL; - extent = 0; - - /* - * Search for pronouns where we have an assigned object or NPC. We - * can't be sure of getting plurality right, and it's not always - * intuitive even in English -- is "a pair of scissors" an "it", or - * a "them"? Because of this, we just treat "it" and "them" equally - * for now. - */ - if (game->it_object != -1 && sc_compare_word (current, "it", 2)) - { - object = game->it_object; - extent = 2; - } - else if (game->it_object != -1 && sc_compare_word (current, "them", 4)) - { - object = game->it_object; - extent = 4; - } - else if (game->him_npc != -1 && sc_compare_word (current, "him", 3)) - { - npc = game->him_npc; - extent = 3; - } - else if (game->her_npc != -1 && sc_compare_word (current, "her", 3)) - { - npc = game->her_npc; - extent = 3; - } - else if (game->it_npc != -1 && sc_compare_word (current, "it", 2)) - { - npc = game->it_npc; - extent = 2; - } - - /* Assign prefix and name to the full object or NPC name, if any. */ - if (object > -1) - { - vt_key[0].string = "Objects"; - vt_key[1].integer = object; - vt_key[2].string = "Prefix"; - prefix = prop_get_string (bundle, "S<-sis", vt_key); - vt_key[2].string = "Short"; - name = prop_get_string (bundle, "S<-sis", vt_key); - } - else if (npc > -1) - { - vt_key[0].string = "NPCs"; - vt_key[1].integer = npc; - vt_key[2].string = "Prefix"; - prefix = prop_get_string (bundle, "S<-sis", vt_key); - vt_key[2].string = "Name"; - name = prop_get_string (bundle, "S<-sis", vt_key); - } - - /* - * If a pronoun was found, prefix and name indicate what to insert, and - * extent shows how much of the buffer to replace with them. - */ - if (prefix && name && extent > 0) - { - sc_char *position; - sc_int prefix_length, name_length, length, final; - - /* - * If not yet allocated, allocate a buffer now, and copy the input - * string into it. Then switch current to the equivalent location - * in the allocated buffer; basic copy-on-write. - */ - if (!buffer) - { - buffer_allocation = strlen (string) + 1; - buffer = (sc_char *)sc_malloc (buffer_allocation); - strcpy (buffer, string); - current = buffer + (current - string); - } - - /* - * If necessary, grow the output buffer for the replacement, - * remembering to adjust current for the new buffer allocated. - * At the same time, note the last character index for the move. - */ - prefix_length = strlen (prefix); - name_length = strlen (name); - length = prefix_length + name_length + 1; - if (length > extent) - { - sc_int offset; - - offset = current - buffer; - buffer_allocation += length - extent; - buffer = (sc_char *)sc_realloc (buffer, buffer_allocation); - current = buffer + offset; - final = length; - } - else - final = extent; - - /* Insert the replacement strings into the buffer. */ - position = buffer + (current - buffer); - memmove (position + length, - position + extent, - buffer_allocation - (current - buffer) - final); - memcpy (position, prefix, prefix_length); - position[prefix_length] = ' '; - memcpy (position + prefix_length + 1, name, name_length); - - /* Adjust current to skip over the replacement. */ - current += length; - - if (uip_trace) - sc_trace ("Parser: pronoun \"%s\"\n", buffer); - } - else - { - /* If no match, advance current over the unmatched word. */ - current += strcspn (current, WHITESPACE); - } - - /* Set current to the next word start. */ - current += strspn (current, WHITESPACE); - } - - /* Return the final string, or NULL if no pronoun replacements. */ - return buffer; +uip_replace_pronouns(sc_gameref_t game, const sc_char *string) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + sc_int buffer_allocation; + sc_char *buffer; + const sc_char *current; + assert(string); + + if (uip_trace) + sc_trace("UIParser: pronoun search \"%s\"\n", string); + + /* Begin with a NULL buffer for lazy allocation. */ + buffer_allocation = 0; + buffer = NULL; + + /* Search for pronouns until no more string remains. */ + current = string + strspn(string, WHITESPACE); + while (current[0] != NUL) { + sc_vartype_t vt_key[3]; + sc_int object, npc, extent; + const sc_char *prefix, *name; + + /* Initially, no object or NPC, no names, and a zero extent. */ + object = npc = -1; + prefix = name = NULL; + extent = 0; + + /* + * Search for pronouns where we have an assigned object or NPC. We + * can't be sure of getting plurality right, and it's not always + * intuitive even in English -- is "a pair of scissors" an "it", or + * a "them"? Because of this, we just treat "it" and "them" equally + * for now. + */ + if (game->it_object != -1 && sc_compare_word(current, "it", 2)) { + object = game->it_object; + extent = 2; + } else if (game->it_object != -1 && sc_compare_word(current, "them", 4)) { + object = game->it_object; + extent = 4; + } else if (game->him_npc != -1 && sc_compare_word(current, "him", 3)) { + npc = game->him_npc; + extent = 3; + } else if (game->her_npc != -1 && sc_compare_word(current, "her", 3)) { + npc = game->her_npc; + extent = 3; + } else if (game->it_npc != -1 && sc_compare_word(current, "it", 2)) { + npc = game->it_npc; + extent = 2; + } + + /* Assign prefix and name to the full object or NPC name, if any. */ + if (object > -1) { + vt_key[0].string = "Objects"; + vt_key[1].integer = object; + vt_key[2].string = "Prefix"; + prefix = prop_get_string(bundle, "S<-sis", vt_key); + vt_key[2].string = "Short"; + name = prop_get_string(bundle, "S<-sis", vt_key); + } else if (npc > -1) { + vt_key[0].string = "NPCs"; + vt_key[1].integer = npc; + vt_key[2].string = "Prefix"; + prefix = prop_get_string(bundle, "S<-sis", vt_key); + vt_key[2].string = "Name"; + name = prop_get_string(bundle, "S<-sis", vt_key); + } + + /* + * If a pronoun was found, prefix and name indicate what to insert, and + * extent shows how much of the buffer to replace with them. + */ + if (prefix && name && extent > 0) { + sc_char *position; + sc_int prefix_length, name_length, length, final; + + /* + * If not yet allocated, allocate a buffer now, and copy the input + * string into it. Then switch current to the equivalent location + * in the allocated buffer; basic copy-on-write. + */ + if (!buffer) { + buffer_allocation = strlen(string) + 1; + buffer = (sc_char *)sc_malloc(buffer_allocation); + strcpy(buffer, string); + current = buffer + (current - string); + } + + /* + * If necessary, grow the output buffer for the replacement, + * remembering to adjust current for the new buffer allocated. + * At the same time, note the last character index for the move. + */ + prefix_length = strlen(prefix); + name_length = strlen(name); + length = prefix_length + name_length + 1; + if (length > extent) { + sc_int offset; + + offset = current - buffer; + buffer_allocation += length - extent; + buffer = (sc_char *)sc_realloc(buffer, buffer_allocation); + current = buffer + offset; + final = length; + } else + final = extent; + + /* Insert the replacement strings into the buffer. */ + position = buffer + (current - buffer); + memmove(position + length, + position + extent, + buffer_allocation - (current - buffer) - final); + memcpy(position, prefix, prefix_length); + position[prefix_length] = ' '; + memcpy(position + prefix_length + 1, name, name_length); + + /* Adjust current to skip over the replacement. */ + current += length; + + if (uip_trace) + sc_trace("Parser: pronoun \"%s\"\n", buffer); + } else { + /* If no match, advance current over the unmatched word. */ + current += strcspn(current, WHITESPACE); + } + + /* Set current to the next word start. */ + current += strspn(current, WHITESPACE); + } + + /* Return the final string, or NULL if no pronoun replacements. */ + return buffer; } @@ -1996,143 +1857,128 @@ uip_replace_pronouns (sc_gameref_t game, const sc_char *string) * earlier ones if there is more than one in the string. */ void -uip_assign_pronouns (sc_gameref_t game, const sc_char *string) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - const sc_char *current; - sc_int saved_ref_object, saved_ref_character; - assert (string); - - if (uip_trace) - sc_trace ("UIParser: pronoun assignment \"%s\"\n", string); - - /* Save var references so we can restore them later. */ - saved_ref_object = var_get_ref_object (vars); - saved_ref_character = var_get_ref_character (vars); - - /* Search for object and NPC names until no more string remains. */ - current = string + strspn (string, WHITESPACE); - while (current[0] != NUL) - { - if (uip_match ("%object% *", current, game)) - { - sc_int count, index_, object; - - /* - * "Disambiguate" by rejecting objects that the player hasn't seen - * or can't see. If the reference is unique, assign to the 'it' - * object pronoun. - */ - count = 0; - object = -1; - for (index_ = 0; index_ < gs_object_count (game); index_++) - { - if (game->object_references[index_] - && gs_object_seen (game, index_) - && obj_indirectly_in_room (game, - index_, gs_playerroom (game))) - { - count++; - object = index_; - } - } - - if (count == 1) - { - game->it_object = object; - game->it_npc = -1; - - if (uip_trace) - sc_trace ("UIParser: object 'it/them' assigned %ld\n", object); - } - } - - if (uip_match ("%character% *", current, game)) - { - sc_int count, index_, npc; - - /* Do the same "disambiguation" as for objects above. */ - count = 0; - npc = -1; - for (index_ = 0; index_ < gs_npc_count (game); index_++) - { - if (game->npc_references[index_] - && gs_npc_seen (game, index_) - && npc_in_room (game, index_, gs_playerroom (game))) - { - count++; - npc = index_; - } - } - - if (count == 1) - { - sc_vartype_t vt_key[3]; - sc_int version, gender; - - /* - * Version 3.8 games lack NPC gender information, so for this - * case set "him"/"her" on each match, and never set "it"; this - * matches the version 3.8 runner. - */ - vt_key[0].string = "Version"; - version = prop_get_integer (bundle, "I<-s", vt_key); - if (version == TAF_VERSION_380) - { - game->him_npc = npc; - game->her_npc = npc; - game->it_npc = -1; - - if (uip_trace) - { - sc_trace ("UIParser: 3.8 pronouns" - " 'him' and 'her' assigned %ld\n", npc); - } - } - else - { - /* Find the NPC gender, so we know the pronoun to assign. */ - vt_key[0].string = "NPCs"; - vt_key[1].integer = npc; - vt_key[2].string = "Gender"; - gender = prop_get_integer (bundle, "I<-sis", vt_key); - - switch (gender) - { - case NPC_MALE: - game->him_npc = npc; - break; - case NPC_FEMALE: - game->her_npc = npc; - break; - case NPC_NEUTER: - game->it_npc = npc; - game->it_object = -1; - break; - default: - sc_error ("uip_assign_pronouns:" - " unknown gender, %ld\n", gender); - } - - if (uip_trace) - sc_trace ("UIParser: NPC 'him/her/it' assigned %ld\n", npc); - } - } - } - - /* - * Advance the string position by a complete word. This saves a lot - * of time -- there's no point looking for an object or NPC name in - * mid-word, and anyway it's not the right thing to do. - */ - current += strcspn (current, WHITESPACE); - current += strspn (current, WHITESPACE); - } - - /* Restore variables references. */ - var_set_ref_object (vars, saved_ref_object); - var_set_ref_character (vars, saved_ref_character); +uip_assign_pronouns(sc_gameref_t game, const sc_char *string) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + const sc_char *current; + sc_int saved_ref_object, saved_ref_character; + assert(string); + + if (uip_trace) + sc_trace("UIParser: pronoun assignment \"%s\"\n", string); + + /* Save var references so we can restore them later. */ + saved_ref_object = var_get_ref_object(vars); + saved_ref_character = var_get_ref_character(vars); + + /* Search for object and NPC names until no more string remains. */ + current = string + strspn(string, WHITESPACE); + while (current[0] != NUL) { + if (uip_match("%object% *", current, game)) { + sc_int count, index_, object; + + /* + * "Disambiguate" by rejecting objects that the player hasn't seen + * or can't see. If the reference is unique, assign to the 'it' + * object pronoun. + */ + count = 0; + object = -1; + for (index_ = 0; index_ < gs_object_count(game); index_++) { + if (game->object_references[index_] + && gs_object_seen(game, index_) + && obj_indirectly_in_room(game, + index_, gs_playerroom(game))) { + count++; + object = index_; + } + } + + if (count == 1) { + game->it_object = object; + game->it_npc = -1; + + if (uip_trace) + sc_trace("UIParser: object 'it/them' assigned %ld\n", object); + } + } + + if (uip_match("%character% *", current, game)) { + sc_int count, index_, npc; + + /* Do the same "disambiguation" as for objects above. */ + count = 0; + npc = -1; + for (index_ = 0; index_ < gs_npc_count(game); index_++) { + if (game->npc_references[index_] + && gs_npc_seen(game, index_) + && npc_in_room(game, index_, gs_playerroom(game))) { + count++; + npc = index_; + } + } + + if (count == 1) { + sc_vartype_t vt_key[3]; + sc_int version, gender; + + /* + * Version 3.8 games lack NPC gender information, so for this + * case set "him"/"her" on each match, and never set "it"; this + * matches the version 3.8 runner. + */ + vt_key[0].string = "Version"; + version = prop_get_integer(bundle, "I<-s", vt_key); + if (version == TAF_VERSION_380) { + game->him_npc = npc; + game->her_npc = npc; + game->it_npc = -1; + + if (uip_trace) { + sc_trace("UIParser: 3.8 pronouns" + " 'him' and 'her' assigned %ld\n", npc); + } + } else { + /* Find the NPC gender, so we know the pronoun to assign. */ + vt_key[0].string = "NPCs"; + vt_key[1].integer = npc; + vt_key[2].string = "Gender"; + gender = prop_get_integer(bundle, "I<-sis", vt_key); + + switch (gender) { + case NPC_MALE: + game->him_npc = npc; + break; + case NPC_FEMALE: + game->her_npc = npc; + break; + case NPC_NEUTER: + game->it_npc = npc; + game->it_object = -1; + break; + default: + sc_error("uip_assign_pronouns:" + " unknown gender, %ld\n", gender); + } + + if (uip_trace) + sc_trace("UIParser: NPC 'him/her/it' assigned %ld\n", npc); + } + } + } + + /* + * Advance the string position by a complete word. This saves a lot + * of time -- there's no point looking for an object or NPC name in + * mid-word, and anyway it's not the right thing to do. + */ + current += strcspn(current, WHITESPACE); + current += strspn(current, WHITESPACE); + } + + /* Restore variables references. */ + var_set_ref_object(vars, saved_ref_object); + var_set_ref_character(vars, saved_ref_character); } } // End of namespace Adrift |