psyclpc/src/parse.c

1823 lines
54 KiB
C

/*---------------------------------------------------------------------------
* Pattern Parser v 3.1 (non-compat mode)
* (C) Copyright 1991 JnA (jna@cd.chalmers.se)
*
*---------------------------------------------------------------------------
* TODO: Improve this efun, make it more general.
* EFUN parse_command
*
* int parse_command (string cmd, object env, string fmt, mixed &var, ...)
* int parse_command (string cmd, object* arr, string fmt, mixed &var, ...)
*
* parse_command() is basically a spiffed up sscanf operating on word basis
* and targeted at recognizing object descriptions from command strings.
*
* The efun takes the command string <cmd> and the object(s) <env>/<arr>
* and tries to match it against the format string <fmt>. Successfully
* matched elements are assigned to the variables <var>....
* The result from the efun is 1 if the command could be fully matched,
* and 0 otherwise.
*
* If the objects are given as a single object <env>, the efun matches
* against the given object and all objects contained therein. Otherwise,
* if the objects are given as an array <arr> of objects, the efun
* matches only against the given objects.
*
* The format string <fmt> consists of words, syntactic markers, and
* %-directives for the values to parse and return in the variables.
* A typical example is " 'get' / 'take' %i " or
* " 'spray' / 'paint' [paint] %i ". The elements in detail are:
*
* 'word': obligatory text
* [word]: optional text
* / : Alternative marker
* %o : Single item, object
* %s : Any text
* %w : Any word
* %p : One of a list of prepositions.
* If the variable associated with %p is used to pass
* a list of words to the efun, the matching will take
* only against this list.
* %l : Living objects
* %i : Any objects
* %d : Number >= 0, or when given textual: 0-99.
*
* A <word> in this context is any sequence of characters not containing
* a space. 'living objects' are searched by calls to the (simul)efuns
* find_player() and find_living(): both functions have to accept a name
* as argument and return the object for this name, or 0 if there
* is none.
*
* The results assigned to the variables by the %-directives are:
*
* %o : returns an object
* %s : returns a string of words
* %w : returns a string of one word
* %p : if passed empty: a string
* if passed as array of words: var[0] is the matched word
* %i : returns an array with the following content:
* [0]: int: the count/number recognized in the object spec
* > 0: a count (e.g. 'three', '4')
* < 0: an ordinal (e.g. 'second', 'third')
* = 0: 'all' or a generic plural such as 'apples'
* [1..]: object: all(!) objects matching the item description.
* In the <env> form this may be the whole
* recursive inventory of the <env> object.
* It is up to the caller to interpret the recognized numeral
* and to apply it on the list of matched objects.
* %l : as %i, except that only living objects are returned.
*
* %i and %l match descriptions like 'three red roses','all nasty bugs'
* or 'second blue sword'.
*
* Note: Patterns of type: "%s %w %i" might not work as one would expect.
* %w will always succeed so the arg corresponding to %s will always be empty.
*
*
* To make the efun useful it must have a certain support from the mudlib:
* it calls a set of functions in objects to get the information it needs
* to parse a string.
*
* 1. string *parse_command_id_list()
* Normal singular names of the object.
*
* 2. string *parse_command_plural_id_list() - optional
* Plural forms of the names returned by 1.
* If this function doesn't exist, the parser tries to pluralize
* the names returned by 1.
*
* 3. string *parse_command_adjectiv_id_list() - optional
* All adjectives associated with this object.
*
* All names and adjectives may consist of several words separated
* by spaces.
*
* These functions should exist in all objects and are therefore best
* put into a mandatory inherit file (e.g. /std/object.c).
*
* In addition the master object may offer the same functions to provide
* reasonable defaults (like 'thing' as generic singular name):
*
* string *parse_command_id_list()
* - Would normally return: ({ "one", "thing" })
*
* string *parse_command_plural_id_list()
* - Would normally return: ({ "ones", "things", "them" })
*
* string *parse_command_adjectiv_id_list()
* - Would normally return ({ "iffish" })
*
* Two additional functions in the master object provide the default
* list of prepositions (needed for %p) and the single 'all' word:
*
* string *parse_command_prepos_list()
* - Would normally return: ({ "in", "on", "under", "behind", "beside" })
*
* string parse_command_all_word()
* - Would normally return: "all"
*---------------------------------------------------------------------------
* TODO: A proper localisation would at least put all the following into the
* TODO:: master object as well.
*
* If you want to use a different language than English, you need to write
* a small file 'parse_local.c' and include it into parse.c at the
* marked position.
*
* The 'parse_local.c' has to contain your localized pluralmaker and
* the textual number words and should look like this:
*
* ---------- SNIP ----------
* #define PARSE_FOREIGN
*
* char *parse_to_plural(str)
* char *str;
* {
*
* * Your own plural converter for your language *
*
* }
*
* * The numberwords below should be replaced for the new language *
*
* static char *ord1[] = {"", "first", "second", "third", "fourth", "fifth",
* "sixth", "seventh", "eighth", "nineth", "tenth",
* "eleventh", "twelfth", "thirteenth", "fourteenth",
* "fifteenth", "sixteenth", "seventeenth",
* "eighteenth","nineteenth"};
*
* static char *ord10[] = {"", "", "twenty","thirty","forty","fifty","sixty",
* "seventy", "eighty","ninety"};
*
* static char *sord10[] = {"", "", "twentieth", "thirtieth", "fortieth",
* "fiftieth", "sixtieth","seventieth", "eightieth",
* "ninetieth"};
*
* static char *num1[] = {"", "one","two","three","four","five","six",
* "seven","eight","nine","ten",
* "eleven","twelve","thirteen","fourteen","fifteen",
* "sixteen", "seventeen","eighteen","nineteen"};
*
* static char *num10[] = {"", "", "twenty","thirty","forty","fifty","sixty",
* "seventy", "eighty","ninety"};
* ---------- SNIP ----------
*---------------------------------------------------------------------------
*/
#include "driver.h"
#if defined(USE_PARSE_COMMAND)
#include "typedefs.h"
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "parse.h"
#include "actions.h"
#include "array.h"
#include "gcollect.h"
#include "interpret.h"
#include "lex.h"
#include "main.h"
#include "mstrings.h"
#include "object.h"
#include "simulate.h"
#include "stdstrings.h"
#include "svalue.h"
#include "wiz_list.h"
#include "xalloc.h"
/* For a localisation of parse_command(),
* #include "parse_local.c"
* here.
*/
/*-------------------------------------------------------------------------*/
/* Some useful string macros
*/
#define PREFIXED(x,y) (strncmp(x, y, strlen(x)) == 0)
/*-------------------------------------------------------------------------*/
/* To make parse_command() reentrant, the module maintains a list
* of previous contexts using this structure:
*/
typedef struct parse_context_s parse_context_t;
struct parse_context_s
{
parse_context_t *previous;
vector_t *id, *plid, *adjid;
vector_t *id_d, *plid_d, *adjid_d, *prepos;
string_t *allword;
/* This context: the lists of ids and such. */
vector_t *wvec, *patvec, *obvec;
/* Next context(!): word, pattern and object vector */
};
/*-------------------------------------------------------------------------*/
/* Arrays holding the constituent words of textual numbers from 0 to 99.
* The numbers are constructed by concatenation.
*/
#ifndef PARSE_FOREIGN
static char *ord1[] = {"", "first", "second", "third", "fourth", "fifth"
, "sixth", "seventh", "eighth", "nineth", "tenth"
, "eleventh", "twelfth", "thirteenth", "fourteenth"
, "fifteenth", "sixteenth", "seventeenth"
, "eighteenth","nineteenth"};
/* The ordinals from 1 to 19, also used to build the ordinals 20..99.
*/
static char *ord10[] = { "", "", "twenty","thirty","forty","fifty","sixty"
, "seventy", "eighty","ninety"};
/* The first word for the ordinals 20..99.
*/
static char *sord10[] = { "", "", "twentieth", "thirtieth", "fortieth"
, "fiftieth", "sixtieth","seventieth", "eightieth"
, "ninetieth"};
/* The ordinals 20, 30, ..., 90.
*/
static char *num1[] = { "", "one","two","three","four","five","six"
, "seven","eight","nine","ten"
, "eleven","twelve","thirteen","fourteen","fifteen"
, "sixteen", "seventeen","eighteen","nineteen"};
/* The numbers 1 to 19, also used to build the numbers 20..99.
*/
static char *num10[] = { "", "", "twenty","thirty","forty","fifty","sixty"
, "seventy", "eighty","ninety"};
/* The first word for the numbers 20..99.
*/
#endif
/*-------------------------------------------------------------------------*/
static svalue_t find_living_closures[2]
= { { T_INVALID }, { T_INVALID } };
/* The closures to the functions 'find_living' and 'find_player',
* which are generated at runtime to be able to find simul-efuns with
* these names.
* TODO: This too should go into a master function.
*/
static parse_context_t *gPrevious_context = NULL;
/* The list of previous contexts.
*/
/* The following variables 'cache' the various lists read from the
* matched objects and the master object. The original values are
* save when parse_command() is called, making the efun re-entrant.
*/
static vector_t *gId_list = NULL;
static vector_t *gPluid_list = NULL;
static vector_t *gAdjid_list = NULL;
/* Arrays of the lists from the objects matched against.
* For example gId_list[2] returns the singular name list for
* the third object.
* The arrays are filled on demand only. svalue-0s denote entries
* yet to fill, svalue-1s are entries where the object doesn't provide
* the particular information.
*/
static vector_t *gId_list_d = NULL;
static vector_t *gPluid_list_d = NULL;
static vector_t *gAdjid_list_d = NULL;
static vector_t *gPrepos_list = NULL;
static string_t *gAllword = NULL;
/* The lists and the 'all' word from the master object.
*/
/*-------------------------------------------------------------------------*/
static object_t *
find_living_object (string_t *name, Bool player)
/* Find the living (<player> is false) or player (<player> is true)
* with the name <name>.
* Return the found object, or NULL if not found.
*
* The functions calls the (simul)efuns 'find_living' resp.
* 'find_player' for this purpose.
*/
{
svalue_t *svp;
/* Get or create the closure for the function to call */
svp = &find_living_closures[player ? 1 : 0];
if (svp->type == T_INVALID)
{
/* We have to create the closure */
symbol_efun(player ? STR_PC_FIND_PLAYER : STR_PC_FIND_LIVING, svp);
}
/* Call the closure */
inter_sp++;
put_ref_string(inter_sp, name);
call_lambda(svp, 1);
pop_stack();
/* In theory this could lose the last ref to the result object.
* In praxis, those objects have more refs.
*/
return inter_sp[1].type != T_OBJECT ? NULL : inter_sp[1].u.ob;
} /* find_living_object() */
/*-------------------------------------------------------------------------*/
#ifdef GC_SUPPORT
void
clear_parse_refs (void)
/* GC support: Clear the references of all memory held by the parser.
*/
{
clear_ref_in_vector( find_living_closures
, sizeof find_living_closures / sizeof(svalue_t)
);
} /* clear_parse_refs() */
/*-------------------------------------------------------------------------*/
void
count_parse_refs (void)
/* GC support: Count the references of all memory held by the parser.
*/
{
count_ref_in_vector( find_living_closures
, sizeof find_living_closures / sizeof(svalue_t)
);
} /* count_parse_refs() */
#endif /* GC_SUPPORT */
#ifndef PARSE_FOREIGN
/*-------------------------------------------------------------------------*/
static string_t *
parse_one_plural (string_t *str)
/* Change the singular noun <str> to a plural and return it.
* The result is either a new string with one reference, or <str> itself
* with an added reference.
*/
{
static char pbuf[100]; /* Result buffer */
char ch, ch2; /* Last two characters in <str> */
size_t sl; /* Last index in <str> */
sl = mstrsize(str);
if (sl < 3 || sl > sizeof(pbuf) - 10)
return str;
sl--;
/* Copy <str> except for the last char into pbuf */
ch = get_txt(str)[sl];
ch2 = get_txt(str)[sl-1];
strcpy(pbuf, get_txt(str)); pbuf[sl] = '\0';
/* Try to make plural based on the last two chars */
switch (ch)
{
case 's':
case 'x':
case 'h':
return new_mstring(strcat(pbuf, "ses"));
case 'y':
return new_mstring(strcat(pbuf, "ies"));
case 'e':
if (ch2 == 'f')
{
pbuf[sl-1] = 0;
return new_mstring(strcat(pbuf, "ves"));
}
}
/* Some known special cases */
if (mstreq(str, STR_PC_CORPSE)) return ref_mstring(STR_PC_CORPSES);
if (mstreq(str, STR_PC_TOOTH)) return ref_mstring(STR_PC_TEETH);
if (mstreq(str, STR_PC_FOOT)) return ref_mstring(STR_PC_FEET);
if (mstreq(str, STR_PC_MAN)) return ref_mstring(STR_PC_MEN);
if (mstreq(str, STR_PC_WOMAN)) return ref_mstring(STR_PC_WOMEN);
if (mstreq(str, STR_PC_CHILD)) return ref_mstring(STR_PC_CHILDREN);
if (mstreq(str, STR_PC_SHEEP)) return ref_mstring(STR_PC_SHEEP);
/* Default: just append 's' */
pbuf[sl] = ch;
return new_mstring(strcat(pbuf, "s"));
} /* parse_one_plural() */
/*-------------------------------------------------------------------------*/
static string_t *
parse_to_plural (string_t *str)
/* Change the singular name <str> to a plural name. The result is a new
* string with one reference.
*
* The algorithm groups the <str> into runs delimited by 'of' (e.g. "the box
* of the king" and pluralizes the last word before each 'of' and the last
* word in the string (giving "the boxes of the kings").
* TODO: TubMud has a good plural maker.
*/
{
vector_t *words;
svalue_t stmp;
string_t *sp;
size_t il;
Bool changed;
/* If it's a single word, it's easy */
if (!(strchr(get_txt(str), ' ')))
return parse_one_plural(str);
/* Multiple words, possible grouped into runs delimited by 'of':
* pluralize the last word in the string, and the last word
* before each 'of'.
*/
words = explode_string(str, STR_SPACE);
for (changed = MY_FALSE, il = 1; (p_int)il < VEC_SIZE(words); il++)
{
if ((mstreq(words->item[il].u.str, STR_PC_OF))
|| (p_int)il+1 == VEC_SIZE(words))
{
/* Got one to pluralize */
sp = parse_one_plural(words->item[il-1].u.str);
if (sp != words->item[il-1].u.str)
{
put_string(&stmp, sp);
transfer_svalue(&words->item[il-1], &stmp);
changed = MY_TRUE;
}
else
free_mstring(sp); /* Reuse the old reference */
}
}
/* If nothing changed, just return a copy of the original */
if (!changed)
{
free_array(words);
return ref_mstring(str);
}
/* We changed it: return the new name */
sp = implode_string(words, STR_SPACE);
free_array(words);
return sp;
} /* parse_to_plural() */
#endif /* PARSE_FOREIGN */
/*-------------------------------------------------------------------------*/
static void
load_lpc_info (size_t ix, object_t *ob)
/* Load the relevant information (singular names, plural names and adjectives)
* for object <ob> into position <ix> of the cache lists, unless already
* loaded.
*
* If the object does not provide plural names, they are synthesized from
* the singular names.
*/
{
Bool make_plural = MY_FALSE; /* TRUE: synthesize plurals */
svalue_t * ret;
if (!ob || ob->flags & O_DESTRUCTED)
return;
/* Get the plural names, if any.
*/
if (gPluid_list
&& VEC_SIZE(gPluid_list) > (p_int)ix
&& gPluid_list->item[ix].type == T_NUMBER
&& gPluid_list->item[ix].u.number == 0
)
{
ret = apply(STR_PC_P_ID_LIST, ob, 0);
if (ret && ret->type == T_POINTER)
assign_svalue_no_free(&gPluid_list->item[ix], ret);
else
{
make_plural = MY_TRUE;
gPluid_list->item[ix].u.number = 1;
}
}
/* Get the singular names and, if desired, synthesize the
* plural names.
*/
if (gId_list
&& VEC_SIZE(gId_list) > (p_int)ix
&& gId_list->item[ix].type == T_NUMBER
&& gId_list->item[ix].u.number == 0
&& !(ob->flags & O_DESTRUCTED) )
{
ret = apply(STR_PC_ID_LIST, ob, 0);
if (ret && ret->type == T_POINTER)
{
assign_svalue_no_free(&gId_list->item[ix], ret);
if (make_plural)
{
/* Pluralize the singular names */
vector_t *tmp, *sing;
svalue_t sval;
string_t *str;
size_t il;
tmp = allocate_array((size_t)VEC_SIZE(ret->u.vec));
if (!tmp)
errorf("(parse_command) Out of memory: array[%lu] for "
"plural names.\n"
, (unsigned long)VEC_SIZE(ret->u.vec));
sing = ret->u.vec;
for (il = 0; (p_int)il < VEC_SIZE(tmp); il++)
{
if (sing->item[il].type == T_STRING)
{
str = parse_to_plural(sing->item[il].u.str);
put_string(&sval, str);
transfer_svalue_no_free(&tmp->item[il],&sval);
}
}
put_array(&sval, tmp);
transfer_svalue_no_free(&gPluid_list->item[ix], &sval);
}
}
else
{
gId_list->item[ix].u.number = 1;
}
}
/* Get the adjectives, if any.
*/
if (gAdjid_list
&& VEC_SIZE(gAdjid_list) > (p_int)ix
&& gAdjid_list->item[ix].type == T_NUMBER
&& gAdjid_list->item[ix].u.number == 0
&& !(ob->flags & O_DESTRUCTED) )
{
ret = apply(STR_PC_ADJ_LIST, ob, 0);
if (ret && ret->type == T_POINTER)
assign_svalue_no_free(&gAdjid_list->item[ix], ret);
else
gAdjid_list->item[ix].u.number = 1;
}
} /* load_lpc_info() */
/*-------------------------------------------------------------------------*/
static void
parse_error_handler (svalue_t *arg UNUSED)
/* The current parse_command() processing was interrupted by an error.
* Clean up the current context and restore the previous context.
*/
{
#ifdef __MWERKS__
# pragma unused(arg)
#endif
parse_context_t *old;
old = gPrevious_context;
/* Delete and free the id arrays. */
if (gId_list)
free_array(gId_list);
if (gPluid_list)
free_array(gPluid_list);
if (gAdjid_list)
free_array(gAdjid_list);
if (gId_list_d)
free_array(gId_list_d);
if (gPluid_list_d)
free_array(gPluid_list_d);
if (gAdjid_list_d)
free_array(gAdjid_list_d);
if (gPrepos_list)
free_array(gPrepos_list);
if (gAllword)
free_mstring(gAllword);
/* Restore the previous lists */
gId_list_d = old->id_d;
gPluid_list_d = old->plid_d;
gAdjid_list_d = old->adjid_d;
gPrepos_list = old->prepos;
gId_list = old->id;
gPluid_list = old->plid;
gAdjid_list = old->adjid;
gAllword = old->allword;
/* Free the local arrays */
free_array(old->wvec);
free_array(old->patvec);
free_array(old->obvec);
gPrevious_context = old->previous;
xfree(old);
} /* parse_error_handler() */
/*-------------------------------------------------------------------------*/
static INLINE void
stack_put (svalue_t *pval, svalue_t *sp, size_t pos, int max)
/* Store the value <pval> into the lvalue <sp>[<pos>].
* If <pval> is NULL, <sp>[<pos>] not a lvalue or <pos> >= <max>,
* nothing happens - which is a good thing as this function stores
* the parsed results into the variables passed to the efun, and we
* never know what the wizards are going to pass there.
*/
{
if (pval && pos < (size_t)max && sp[pos].type == T_LVALUE)
transfer_svalue(sp[pos].u.lvalue, pval);
} /* stack_put() */
/*-------------------------------------------------------------------------*/
static svalue_t *
slice_words (vector_t *wvec, size_t from, size_t to)
/* Return an imploded string of words from <wvec>[<from>..<to>] as tabled
* string svalue.
* Return NULL if there is nothing to slice.
*/
{
vector_t *slice;
string_t *tx;
static svalue_t stmp;
if (from > to)
return NULL;
slice = slice_array(wvec, from, to);
if (VEC_SIZE(slice))
tx = implode_string(slice, STR_SPACE);
else
tx = NULL;
free_array(slice);
if (tx)
{
put_string(&stmp, make_tabled(tx));
return &stmp;
}
else
return NULL;
} /* slice_words() */
/*-------------------------------------------------------------------------*/
static int
find_string (string_t *str, vector_t *wvec, size_t *cix_in)
/* Test if the (multi-word) string <str> exists in the array of words <wvec>
* at or after position <cix_in>.
* If found, return the starting position in <wvec> and set <cix_in> to
* the position of the last word of the found string.
* If not round, return -1, <cix_in> will be set to the end of <wvec>.
*/
{
int fpos;
string_t *p1;
char *p2;
vector_t *split;
/* Step through wvec and look for a match */
for (; (p_int)*cix_in < VEC_SIZE(wvec); (*cix_in)++)
{
p1 = wvec->item[*cix_in].u.str;
/* Quick test: first character has to match */
if (get_txt(p1)[0] != get_txt(str)[0])
continue;
if (mstreq(p1, str)) /* str was one word and we found it */
return (int)*cix_in;
if (!(p2 = strchr(get_txt(str), ' ')))
continue;
/* If str is a multiword string and we need to make some special checks
*/
if ((p_int)*cix_in + 1 == VEC_SIZE(wvec))
continue;
split = explode_string(str, STR_SPACE);
/* Now: wvec->size - *cix_in = 2: One extra word
* = 3: Two extra words
*/
if (!split || VEC_SIZE(split) > (VEC_SIZE(wvec) - (p_int)*cix_in))
{
if (split)
free_array(split);
continue;
}
/* Test if the following words match the string */
fpos = (int)*cix_in;
for (; *cix_in < (size_t)(VEC_SIZE(split) + fpos); (*cix_in)++)
{
if (!mstreq(split->item[*cix_in-fpos].u.str,
wvec->item[*cix_in].u.str))
break;
}
/* If all of split matched, we found it */
if ((p_int)(*cix_in - fpos) == VEC_SIZE(split))
return fpos;
/* Not found: continue search */
*cix_in = fpos;
}
/* Not found */
return -1;
} /* find_string() */
/*-------------------------------------------------------------------------*/
static int
member_string (string_t *str, vector_t *svec)
/* Test if string <str> is member of the array <svec>.
* Return the position if found, and -1 otherwise.
*/
{
size_t il;
if (!svec)
return -1;
for (il = 0; (p_int)il < VEC_SIZE(svec); il++)
{
if (svec->item[il].type != T_STRING)
continue;
if (mstreq(svec->item[il].u.str, str))
return (int)il;
}
return -1;
} /* member_string() */
/*-------------------------------------------------------------------------*/
static Bool
check_adjectiv (size_t obix, vector_t *wvec, size_t from, size_t to)
/* Check if the command words <wvec>[<from>..<to>] match the adjectives
* for object <obix>.
* Return TRUE if yes.
*/
{
size_t il;
size_t sum; /* Total length of command words tested */
size_t back;
Bool fail; /* TRUE if not found */
string_t *adstr;
char *adstrp;
vector_t *ids; /* Adj list of the object */
/* Get the objects adj-list if existing */
if (gAdjid_list->item[obix].type == T_POINTER)
ids = gAdjid_list->item[obix].u.vec;
else
ids = NULL;
/* Scan the given command words, sum up their length and
* test if all of them match the adjectives given.
*/
for (sum = 0, fail = MY_FALSE, il = from; il <= to; il++)
{
sum += mstrsize(wvec->item[il].u.str) + 1;
if ((member_string(wvec->item[il].u.str, ids) < 0)
&& (member_string(wvec->item[il].u.str, gAdjid_list_d) < 0))
{
fail = MY_TRUE;
}
}
/* Simple case: all adjs were single words and matched.
*/
if (!fail)
return MY_TRUE;
if (from == to)
return MY_FALSE;
/* It could be that some of the adjectives provided by the object are
* multi-words; in that case the above loop would signal a mismatch.
*
* To find these, concatenate the command words with spaces and
* test them against the single adjective strings.
* TODO: This test could be implemented faster.
*/
adstr = NULL;
adstrp = xalloc(sum); /* Workspace */
/* Test the adjectives one after the other */
for (il = from; il < to;)
{
/* For every adjective, perform a greedy match first, ie
* try to match the longer concatenated strings before
* the shorter ones.
*/
for (back = to; back > il; back--)
{
/* Catenate the adjective from the command words */
adstrp[0] = '\0';
for (sum = il; sum <= back; sum++)
{
if (sum > il)
strcat(adstrp, " ");
strcat(adstrp, get_txt(wvec->item[sum].u.str));
}
adstr = new_mstring(adstrp);
if ((member_string(adstr, ids) >= 0)
|| (member_string(adstr, gAdjid_list_d) >= 0))
{
/* Found: continue search after this matched adjective */
il = back + 1;
break;
}
/* Not found: abort */
mstring_free(adstr);
xfree(adstrp);
return MY_FALSE;
}
}
/* Found: clean up and return */
mstring_free(adstr);
xfree(adstrp);
return MY_TRUE;
} /* check_adjectiv() */
/*-------------------------------------------------------------------------*/
static svalue_t *
number_parse( vector_t *obvec UNUSED /* in: array of objects to match against */
, vector_t *wvec /* in: array of words to match */
, size_t *cix_in /* in-out: position in wvec */
, Bool *fail /* out: TRUE on mismatch */
)
/* Interpret the words in wvec[cix_in] as numeric descriptor, parse it
* and return an int-svalue with the result:
* > 0: a number ('one', 'two', 'three', or the number given)
* < 0: an ordinal ('first', 'second')
* = 0: any ('zero', 0, the gAllword)
* On failure, return NULL.
*
* On return, <fail> is set to the success state of the match, and <cix_in>
* has been set past the parsed number.
*/
{
#ifdef __MWERKS__
# pragma unused(obvec)
#endif
static svalue_t stmp; /* Result buffer */
size_t cix;
int ten, ones, num;
cix = *cix_in; *fail = MY_FALSE;
ones = 0;
/* First try to parse the number in digit representation */
if (sscanf(get_txt(wvec->item[cix].u.str), "%d", &num))
{
if (num >= 0)
{
(*cix_in)++;
put_number(&stmp, num);
return &stmp;
}
*fail = MY_TRUE;
return NULL; /* Only nonnegative numbers allowed */
}
/* Is it the 'all' word? */
if (gAllword && mstreq(wvec->item[cix].u.str, gAllword))
{
(*cix_in)++;
put_number(&stmp, 0);
return &stmp;
}
/* Test the number against every known textual number.
*/
for (ten = 0; ten < 10; ten++)
{
char *second;
/* Test if the first part of the word matches */
if (!PREFIXED(num10[ten], get_txt(wvec->item[cix].u.str)))
continue;
/* Yup, now match the rest */
second = get_txt(wvec->item[cix].u.str) + strlen(num10[ten]);
for (ones = 0; ones < 10; ones++)
{
char *tmp;
tmp = (ten>1) ? num1[ones] : num1[ten*10+ones];
if (!strcmp(second, tmp))
{
(*cix_in)++;
put_number(&stmp, ten*10+ones);
return &stmp;
}
} /* for (ones) */
} /* for (ten) */
/* Test the number against every known textual ordinal.
*/
for (ten = 0; ten < 10; ten++)
{
char *second;
/* Multiples of 10 have their own words */
if (!strcmp(sord10[ten], get_txt(wvec->item[cix].u.str)))
{
(*cix_in)++;
put_number(&stmp, -(ten*10+ones));
return &stmp;
}
/* Test if the first part of the word matches */
if (!PREFIXED(ord10[ten], get_txt(wvec->item[cix].u.str)))
continue;
/* Yup, now match the rest */
second = get_txt(wvec->item[cix].u.str) + strlen(ord10[ten]);
for(ones = 1; ones < 10; ones++)
{
char *tmp;
tmp = (ten > 1) ? ord1[ones] : ord1[ten*10+ones];
if (!strcmp(second, tmp))
{
(*cix_in)++;
put_number(&stmp, -(ten*10+ones));
return &stmp;
}
}
}
/* Nothing matches */
*fail = MY_TRUE;
return NULL;
} /* number_parse() */
/*-------------------------------------------------------------------------*/
static Bool
match_object (size_t obix, vector_t *wvec, size_t *cix_in, Bool *plur)
/* Test if a given object <obix> matches the description <wvec>[<cix_in>..].
* If <plur> is TRUE, only the plural description is considered, otherwise
* both plural and singular.
*
* Return TRUE if the object matches; <plur> will be set to true if the
* plural description matched, and <cix_in> will point to the word after
* the matched description.
* Return FALSE if it didn't match.
*/
{
vector_t *ids; /* Id-list to test against */
int cplur; /* Which id-list to test (0..3) */
size_t il, old_cix;
int pos;
string_t *str;
/* Loop over the four lists of ids */
for (cplur = *plur ? 2 : 0; cplur < 4; cplur++)
{
switch (cplur)
{
case 0: /* Global singular ids */
if (!gId_list_d)
continue;
ids = gId_list_d;
break;
case 1: /* Object singular ids */
if (!gId_list
|| VEC_SIZE(gId_list) <= (p_int)obix
|| gId_list->item[obix].type != T_POINTER)
continue;
ids = gId_list->item[obix].u.vec;
break;
case 2: /* Global plural ids */
if (!gPluid_list_d)
continue;
ids = gPluid_list_d;
break;
case 3: /* Object plural ids */
if (!gPluid_list
|| VEC_SIZE(gPluid_list) <= (p_int)obix
|| gPluid_list->item[obix].type != T_POINTER)
continue;
ids = gPluid_list->item[obix].u.vec;
break;
default:
fatal("match_object() called with invalid arguments\n");
}
if (!ids)
fatal("match_object(): internal error\n");
/* Loop over the ids and find a match */
for (il = 0; (p_int)il < VEC_SIZE(ids); il++)
{
if (ids->item[il].type == T_STRING)
{
str = ids->item[il].u.str; /* A given id of the object */
old_cix = *cix_in;
if ((pos = find_string(str, wvec, cix_in)) >= 0)
{
/* Id matched, now check a possible adjective */
if ((size_t)pos == old_cix
|| check_adjectiv(obix, wvec, old_cix, pos-1))
{
if (cplur > 1)
*plur = MY_TRUE;
return MY_TRUE;
}
}
*cix_in = old_cix;
}
} /* for(il) */
} /* for (cplur) */
/* Doesn't match */
return MY_FALSE;
} /* match_object() */
/*-------------------------------------------------------------------------*/
static svalue_t *
item_parse (vector_t *obvec, vector_t *wvec, size_t *cix_in, Bool *fail)
/* Try to match as many objects in <obvec> as possible onto the description
* given in commandvector <wvec>[<cix_in>..].
* Result is a vector with the found objects, and the first element is
* a number returning a found numeral: 0 for 'all' or a generic plural,
* > 0: for a numeral 'one', 'two' etc, < 0 for an ordinal 'first', 'second', etc.
* <cix_in> is updated and <fail> is set to FALSE.
*
* On failure, return NULL, update <cix_in> and set <fail> to TRUE.
*/
{
static svalue_t stmp; /* Result buffer */
vector_t *tmp;
vector_t *ret;
svalue_t *pval;
size_t cix, tix;
size_t max_cix; /* Highest cix used in matching */
Bool plur_flag; /* Plural numeral */
Bool match_all; /* 'all' numeral */
size_t obix;
/* Intermediate result vector */
tmp = allocate_array(VEC_SIZE(obvec) + 1);
/* Try to parse a numeral */
if ( NULL != (pval = number_parse(obvec, wvec, cix_in, fail)) )
assign_svalue_no_free(&tmp->item[0], pval);
if (pval && pval->u.number > 1)
{
plur_flag = MY_TRUE;
match_all = MY_FALSE;
}
else if (pval && pval->u.number == 0)
{
plur_flag = MY_TRUE;
match_all = MY_TRUE;
}
else
{
plur_flag = MY_FALSE;
match_all = MY_TRUE;
}
/* Scan the object vector and try to match each one of it */
for (max_cix = *cix_in, tix = 1, obix = 0; (p_int)obix < VEC_SIZE(obvec); obix++)
{
*fail = MY_FALSE;
cix = *cix_in;
if (obvec->item[obix].type != T_OBJECT)
continue;
/* Command was something like "get all": accept all objects */
if ((p_int)cix == VEC_SIZE(wvec) && match_all)
{
assign_svalue_no_free(&tmp->item[tix++], &obvec->item[obix]);
continue;
}
/* Get the id-info for this object */
load_lpc_info(obix, obvec->item[obix].u.ob);
if (obvec->item[obix].u.ob->flags & O_DESTRUCTED) /* Oops */
continue;
if (match_object(obix, wvec, &cix, &plur_flag))
{
assign_svalue_no_free(&tmp->item[tix++],&obvec->item[obix]);
max_cix = (max_cix < cix) ? cix : max_cix;
}
}
if (tix < 2)
{
/* No object matched: failure */
*fail = MY_TRUE;
free_array(tmp);
if (pval)
(*cix_in)--;
return NULL;
}
else
{
/* We got matches: now compute the results */
if ((p_int)(*cix_in) < VEC_SIZE(wvec))
*cix_in = max_cix + 1;
ret = slice_array(tmp, 0, tix-1);
if (!pval)
{
put_number(ret->item, plur_flag ? 0 : 1);
}
free_array(tmp);
}
/* Return the result */
put_array(&stmp, ret);
return &stmp;
} /* item_parse() */
/*-------------------------------------------------------------------------*/
static svalue_t *
living_parse (vector_t *obvec, vector_t *wvec, size_t *cix_in, Bool *fail)
/* Try to match as many living objects in <obvec> as possible onto the
* description given in commandvector <wvec>[<cix_in>..].
* Result is a vector with the found objects, and the first element is
* a number returning a found numeral: 0 for 'all' or a generic plural,
* > 0: for a numeral 'one', 'two' etc, < 0 for an ordinal 'first', 'second',
* etc.
* <cix_in> is updated and <fail> is set to FALSE.
*
* On failure, return NULL, update <cix_in> and set <fail> to TRUE.
*/
{
static svalue_t stmp; /* Result buffer */
vector_t *live;
svalue_t *pval;
object_t *ob;
size_t obix, tix;
*fail = MY_FALSE;
/* Fill live with all living objects from <obvec> */
tix = 0;
live = allocate_array(VEC_SIZE(obvec));
for (obix = 0; (p_int)obix < VEC_SIZE(obvec); obix++)
{
if (obvec->item[obix].type != T_OBJECT)
continue;
if (obvec->item[obix].u.ob->flags & O_ENABLE_COMMANDS)
assign_svalue_no_free(&live->item[tix++], &obvec->item[obix]);
}
/* If we have living objects, simply call item_parse() on
* that array. If that succeeds, we have our result.
*/
if (tix)
{
pval = item_parse(live, wvec, cix_in, fail);
if (pval)
{
free_array(live);
return pval;
}
}
free_array(live);
/* We can't find a matching living object in obvec, but
* maybe the command names a player or living by name.
*/
ob = find_living_object(wvec->item[*cix_in].u.str, MY_TRUE);
if (!ob)
ob = find_living_object(wvec->item[*cix_in].u.str, MY_FALSE);
if (ob)
{
put_ref_object(&stmp, ob, "living_parse");
(*cix_in)++;
return &stmp;
}
/* Not found */
*fail = MY_TRUE;
return NULL;
} /* living_parse() */
/*-------------------------------------------------------------------------*/
static svalue_t *
single_parse (vector_t *obvec, vector_t *wvec, size_t *cix_in, Bool *fail)
/* Find the first object in <obvec> matching the description in <wvec>[<cix_in>..]
* and return it as an object svalue. <cix_in> is updated and <fail> is set
* to false.
*
* If not found, return NULL, update <cix_in> and set <fail> to true.
*/
{
size_t cix, obix;
Bool plur_flag;
svalue_t *osvp;
/* Loop over the list of objects */
osvp = obvec->item;
for (obix = 0; (p_int)obix < VEC_SIZE(obvec); obix++, osvp++)
{
if (osvp->type != T_OBJECT)
continue;
*fail = MY_FALSE;
cix = *cix_in;
load_lpc_info(obix,osvp->u.ob);
if (osvp->u.ob->flags & O_DESTRUCTED) /* Oops */
continue;
plur_flag = MY_FALSE;
if (match_object(obix, wvec, &cix, &plur_flag))
{
*cix_in = cix+1;
(void)ref_object(osvp->u.ob, "single_parse");
return osvp;
}
}
/* Not found */
*fail = MY_TRUE;
return NULL;
} /* single_parse() */
/*-------------------------------------------------------------------------*/
static svalue_t *
prepos_parse (vector_t *wvec, size_t *cix_in, Bool *fail, svalue_t *prepos)
/* Match the commandwords <wvec>[<cix_in>..] against a list of prepositions.
* On return, <cix_in> has been updated and <fail> gives the success.
*
* If <prepos> is NULL or not an array of strings, the match takes place
* against the prepositions given by the master object. The result will be
* a static string-svalue with the matched preposition.
*
* If <prepos> is an array of strings, it the list of prepositions matched
* against. On success, the result is the <prepos> array and first element
* of the array will be the matched preposition (which has been swapped against
* the original content of that element).
*/
{
static svalue_t stmp;
vector_t *pvec, *tvec;
string_t *tmp;
size_t pix, tix;
/* Determine list to match against */
if (!prepos || prepos->type != T_POINTER)
pvec = gPrepos_list;
else
pvec = prepos->u.vec;
for (pix = 0; (p_int)pix < VEC_SIZE(pvec); pix++)
{
if (pvec->item[pix].type != T_STRING)
continue;
tmp = pvec->item[pix].u.str;
if (!strchr(get_txt(tmp),' '))
{
/* A single word match */
if (mstreq(tmp, wvec->item[*cix_in].u.str))
{
(*cix_in)++;
break;
}
}
else
{
/* Multiword match */
tvec = explode_string(tmp, STR_SPACE);
for (tix = 0; (p_int)tix < VEC_SIZE(tvec); tix++)
{
if ((p_int)(*cix_in+tix) >= VEC_SIZE(wvec)
|| (!mstreq(wvec->item[*cix_in+tix].u.str, tvec->item[tix].u.str))
)
break;
}
tix = ((p_int)tix == VEC_SIZE(tvec)) ? 1 : 0;
if (tix)
(*cix_in) += VEC_SIZE(tvec);
free_array(tvec);
if (tix)
break;
}
}
if ((p_int)pix == VEC_SIZE(pvec))
{
*fail = MY_TRUE;
}
else if (pvec != gPrepos_list)
{
/* We received a prepos list: now move the found preposition
* to the front.
*/
stmp = pvec->item[0];
pvec->item[0] = pvec->item[pix];
pvec->item[pix] = stmp;
ref_array(pvec); /* The caller will free the prepos at some point. */
*fail = MY_FALSE;
}
else
{
/* We found a preposition in the master's list. */
assign_svalue_no_free(&stmp, &pvec->item[pix]);
return &stmp;
}
return prepos;
} /* prepos_parse() */
/*-------------------------------------------------------------------------*/
static svalue_t *
one_parse ( vector_t *obvec /* in: array of objects to match against */
, string_t *pat /* in: word */
, vector_t *wvec /* in: array of command words */
, size_t *cix_in /* in-out: position in wvec */
, Bool *fail /* out: TRUE if mismatch */
, svalue_t *prep_param /* current lvalue stack position for %p */
)
/* Match a single pattern, consuming the words from the wvec.
* On return, <fail> gives the status of the match, and <pix_in> and <cix_in>
* have been updated. Direct result is the next matched value in
* a static buffer, or NULL.
*
* The function does not handle alternatives or '%s', and is called
* from sub_parse().
*/
{
static svalue_t stmp; /* The result buffer */
char ch; /* Command character */
svalue_t *pval;
char *str1, *str2;
/* Nothing left to parse? */
if ((p_int)*cix_in == VEC_SIZE(wvec))
{
*fail = MY_TRUE;
return NULL;
}
/* Get the command character */
ch = get_txt(pat)[0];
if (ch == '%')
{
ch = get_txt(pat)[1];
}
pval = NULL;
/* Interpret the possible patterns */
switch (ch)
{
case 'i': case 'I': /* Match an item */
pval = item_parse(obvec, wvec, cix_in, fail);
break;
case 'l': case 'L': /* Match a living item */
pval = living_parse(obvec, wvec, cix_in, fail);
break;
case 's': case 'S':
*fail = MY_FALSE; /* This is a double %s in pattern, skip it */
break;
case 'w': case 'W': /* Match the next word */
put_string(&stmp, make_tabled_from(wvec->item[*cix_in].u.str));
pval = &stmp;
(*cix_in)++;
*fail = MY_FALSE;
break;
case 'o': case 'O': /* Match an object */
pval = single_parse(obvec, wvec, cix_in, fail);
break;
case 'p': case 'P': /* Match a preposition */
pval = prepos_parse(wvec, cix_in, fail, prep_param);
break;
case 'd': case 'D': /* Match a number */
pval = number_parse(obvec, wvec, cix_in, fail);
break;
case '\'': /* Match a required word */
str1 = &get_txt(pat)[1];
str2 = get_txt(wvec->item[*cix_in].u.str);
if (strncmp(str1, str2, strlen(str1)-1) == 0
&& strlen(str1) == strlen(str2)+1)
{
*fail = MY_FALSE;
(*cix_in)++;
}
else
*fail = MY_TRUE;
break;
case '[': /* Match an optional word */
str1 = &get_txt(pat)[1];
str2 = get_txt(wvec->item[*cix_in].u.str);
if (strncmp(str1, str2, strlen(str1)-1) == 0
&& strlen(str1) == strlen(str2)+1)
{
(*cix_in)++;
}
*fail = MY_FALSE;
break;
default:
*fail = MY_FALSE; /* Skip invalid patterns */
}
return pval;
} /* one_parse() */
/*-------------------------------------------------------------------------*/
static svalue_t *
sub_parse ( vector_t *obvec /* in: array of objects to match against */
, vector_t *patvec /* in: array of pattern elements */
, size_t *pix_in /* in-out: position in patvec */
, vector_t *wvec /* in: array of command words */
, size_t *cix_in /* in-out: position in wvec */
, Bool *fail /* out: TRUE if mismatch */
, svalue_t *sp /* current lvalue stack position for %p */
)
/* Parse a vector of words against a pattern from the given position.
* On return, <fail> gives the status of the match, and <pix_in> and <cix_in>
* have been updated. Direct result is the next matched value, or NULL.
*
* The function handles all pattern elements except '%s' and is called
* by e_parse_command().
*/
{
size_t cix, pix; /* Local positions */
Bool subfail;
svalue_t *pval;
/* There must be something left to match */
if ((p_int)*cix_in == VEC_SIZE(wvec))
{
*fail = MY_TRUE;
return NULL;
}
cix = *cix_in; pix = *pix_in; subfail = MY_FALSE;
/* Try to parse a single pattern element */
pval = one_parse( obvec, patvec->item[pix].u.str
, wvec, &cix, &subfail, sp);
/* If no match (so far), try the next alternative.
* There must be at least one '/' following in the pattern array.
*/
while (subfail)
{
pix++;
cix = *cix_in;
while ((p_int)pix < VEC_SIZE(patvec)
&& mstreq(patvec->item[pix].u.str, STR_SLASH))
{
subfail = MY_FALSE;
pix++;
}
if (!subfail && (p_int)pix < VEC_SIZE(patvec))
pval = one_parse( obvec, patvec->item[pix].u.str, wvec, &cix
, &subfail, sp);
else
{
/* No '/': failure */
*fail = MY_TRUE; *pix_in = pix-1;
return NULL;
}
}
/* We have a match: skip remaining alternatives */
if ((p_int)pix+1 < VEC_SIZE(patvec)
&& mstreq(patvec->item[pix+1].u.str, STR_SLASH))
{
while ((p_int)pix+1 < VEC_SIZE(patvec)
&& mstreq(patvec->item[pix+1].u.str, STR_SLASH))
{
pix += 2;
}
pix++; /* Skip last alternate after last '/' */
if ((p_int)pix >= VEC_SIZE(patvec))
pix = VEC_SIZE(patvec)-1;
}
/* That's it: return the result */
*cix_in = cix;
*pix_in = pix;
*fail = MY_FALSE;
return pval;
} /* sub_parse() */
/*-------------------------------------------------------------------------*/
Bool
e_parse_command ( string_t *cmd /* Command to parse */
, svalue_t *ob_or_array /* Object or array of objects */
, string_t *pattern /* Special parsing pattern */
, svalue_t *stack_args /* Pointer to lvalue args on stack */
, int num_arg /* Number of lvalues on stack */
)
/* EFUN parse_command()
*
* This function implements the parse_command() efun, called from interpret.c.
* Result is TRUE on success, and FALSE otherwise.
*/
{
static svalue_t error_handler_addr = { T_ERROR_HANDLER };
vector_t *obvec = NULL; /* Objects to match against */
vector_t *patvec; /* Elements in pattern <pattern> */
vector_t *wvec; /* Words in command <cmd> */
parse_context_t *old;
Bool fail; /* TRUE if the match failed */
size_t pix; /* Index in patvec */
size_t cix; /* Index in wvec */
size_t six; /* Index to the lvalues on the stack */
svalue_t *pval; /* Result from a subparse */
/* Pattern and commands can not be empty
*/
if (!mstrsize(cmd) || !mstrsize(pattern))
return MY_FALSE;
/* Prepare some variables */
xallocate(old, sizeof *old, "parse context");
wvec = explode_string(cmd, STR_SPACE);
if (!wvec)
wvec = allocate_array(0);
patvec = explode_string(pattern, STR_SPACE);
if (!patvec)
patvec = allocate_array(0);
if (ob_or_array->type == T_POINTER)
{
/* There might be more references to this array, which could cause
* real nightmares if load_lpc_info() changes the array.
*/
check_for_destr(ob_or_array->u.vec);
obvec =
slice_array(ob_or_array->u.vec, 0, VEC_SIZE(ob_or_array->u.vec) - 1);
}
else if (ob_or_array->type == T_OBJECT)
{
obvec = deep_inventory(ob_or_array->u.ob, /* take_top: */ MY_TRUE, /* depth: */ 0);
}
else
{
free_array(wvec);
free_array(patvec);
xfree(old);
errorf("Bad second argument to parse_command()\n");
}
/* Save the previous context and set up the error handler */
old->id = gId_list;
old->plid = gPluid_list;
old->adjid = gAdjid_list;
old->id_d = gId_list_d;
old->plid_d = gPluid_list_d;
old->adjid_d = gAdjid_list_d;
old->prepos = gPrepos_list;
old->allword = gAllword;
old->wvec = wvec;
old->patvec = patvec;
old->obvec = obvec;
old->previous = gPrevious_context;
gPrevious_context = old;
push_error_handler(parse_error_handler, &error_handler_addr);
/* Make space for the list arrays */
gId_list = allocate_array(VEC_SIZE(obvec));
gPluid_list = allocate_array(VEC_SIZE(obvec));
gAdjid_list = allocate_array(VEC_SIZE(obvec));
/* Get the default ids of 'general references' from master object
*/
pval = apply_master(STR_PC_ID_LIST, 0);
if (pval && pval->type == T_POINTER)
{
gId_list_d = ref_array(pval->u.vec);
}
else
gId_list_d = NULL;
pval = apply_master(STR_PC_P_ID_LIST, 0);
if (pval && pval->type == T_POINTER)
{
gPluid_list_d = ref_array(pval->u.vec);
}
else
gPluid_list_d = NULL;
pval = apply_master(STR_PC_ADJ_LIST, 0);
if (pval && pval->type == T_POINTER)
{
gAdjid_list_d = ref_array(pval->u.vec);
}
else
gAdjid_list_d = NULL;
pval = apply_master(STR_PC_PREPOS, 0);
if (pval && pval->type == T_POINTER)
{
gPrepos_list = ref_array(pval->u.vec);
}
else
gPrepos_list = allocate_array(0);
pval = apply_master(STR_PC_ALLWORD,0);
if (pval && pval->type == T_STRING)
gAllword = ref_mstring(pval->u.str);
else
gAllword = NULL;
/* Loop through the pattern. Handle %s but not '/'
*/
for (six = 0, cix = 0, fail = MY_FALSE, pix = 0
; (p_int)pix < VEC_SIZE(patvec); pix++)
{
pval = NULL;
fail = MY_FALSE;
if (mstreq(patvec->item[pix].u.str, STR_PERCENT_S))
{
/* If at the end of the pattern, %s matches everything left
* in the wvec.
* Otherwise it matches everything up to the next pattern
* element.
*/
if ((p_int)pix == VEC_SIZE(patvec)-1)
{
pval = slice_words(wvec, cix, VEC_SIZE(wvec)-1);
cix = VEC_SIZE(wvec);
}
else
{
size_t fword, ocix, fpix;
ocix = fword = cix;
fpix = ++pix;
/* Try parsing the next pattern element at increasingly
* further distances from the current position in wvec.
* The loop ends when a match is found, or wvec is exhausted.
*/
do {
fail = MY_FALSE;
pval = sub_parse(obvec, patvec, &pix, wvec, &cix, &fail
, (six < (size_t)num_arg) ? stack_args[six].u.lvalue
: 0);
if (fail)
{
cix = ++ocix;
pix = fpix;
}
} while (fail && (p_int)cix < VEC_SIZE(wvec));
/* If we failed to find a match, the whole pattern string
* doesn't match. Otherwise store the wvec slice between
* the current position and the match into the next
* variable.
*/
if (!fail)
{
stack_put(pval, stack_args, six+1, num_arg);
pval = slice_words(wvec, fword, ocix-1);
stack_put(pval, stack_args, six++, num_arg);
pval = NULL;
}
}
}
else if (!mstreq(patvec->item[pix].u.str, STR_SLASH))
{
/* Everything else is handled by sub_parse() */
pval = sub_parse( obvec, patvec, &pix, wvec, &cix, &fail
, (six < (size_t)num_arg) ? stack_args[six].u.lvalue : 0);
}
if (!fail && pval)
stack_put(pval, stack_args, six++, num_arg);
else if (fail)
break;
} /* for() */
/* Also fail when there are words left to parse and pattern exhausted.
*/
if ((p_int)cix < VEC_SIZE(wvec))
fail = MY_TRUE;
pop_stack(); /* Clean up via the error handler */
return !fail;
} /* e_parse_command() */
#endif /* USE_PARSE_COMMAND */
/***************************************************************************/