ldmud-3.2.9/doc/
ldmud-3.2.9/doc/efun/
ldmud-3.2.9/mud/
ldmud-3.2.9/mud/heaven7/
ldmud-3.2.9/mud/heaven7/lib/
ldmud-3.2.9/mud/lp-245/
ldmud-3.2.9/mud/lp-245/banish/
ldmud-3.2.9/mud/lp-245/doc/
ldmud-3.2.9/mud/lp-245/doc/examples/
ldmud-3.2.9/mud/lp-245/doc/sefun/
ldmud-3.2.9/mud/lp-245/log/
ldmud-3.2.9/mud/lp-245/obj/Go/
ldmud-3.2.9/mud/lp-245/players/lars/
ldmud-3.2.9/mud/lp-245/room/death/
ldmud-3.2.9/mud/lp-245/room/maze1/
ldmud-3.2.9/mud/lp-245/room/sub/
ldmud-3.2.9/mud/lp-245/secure/
ldmud-3.2.9/mud/morgengrauen/
ldmud-3.2.9/mud/morgengrauen/lib/
ldmud-3.2.9/mud/sticklib/
ldmud-3.2.9/mud/sticklib/src/
ldmud-3.2.9/mudlib/uni-crasher/
ldmud-3.2.9/pkg/
ldmud-3.2.9/pkg/debugger/
ldmud-3.2.9/pkg/diff/
ldmud-3.2.9/pkg/misc/
ldmud-3.2.9/src/autoconf/
ldmud-3.2.9/src/bugs/
ldmud-3.2.9/src/bugs/MudCompress/
ldmud-3.2.9/src/bugs/b-020916-files/
ldmud-3.2.9/src/bugs/doomdark/
ldmud-3.2.9/src/bugs/ferrycode/ferry/
ldmud-3.2.9/src/bugs/ferrycode/obj/
ldmud-3.2.9/src/bugs/psql/
ldmud-3.2.9/src/done/
ldmud-3.2.9/src/done/order_alist/
ldmud-3.2.9/src/done/order_alist/obj/
ldmud-3.2.9/src/done/order_alist/room/
ldmud-3.2.9/src/gcc/
ldmud-3.2.9/src/gcc/2.7.0/
ldmud-3.2.9/src/gcc/2.7.1/
ldmud-3.2.9/src/hosts/
ldmud-3.2.9/src/hosts/GnuWin32/
ldmud-3.2.9/src/hosts/amiga/NetIncl/
ldmud-3.2.9/src/hosts/amiga/NetIncl/netinet/
ldmud-3.2.9/src/hosts/amiga/NetIncl/sys/
ldmud-3.2.9/src/hosts/i386/
ldmud-3.2.9/src/hosts/msdos/byacc/
ldmud-3.2.9/src/hosts/msdos/doc/
ldmud-3.2.9/src/hosts/os2/
ldmud-3.2.9/src/hosts/win32/
ldmud-3.2.9/src/util/
ldmud-3.2.9/src/util/erq/
ldmud-3.2.9/src/util/indent/hosts/next/
ldmud-3.2.9/src/util/xerq/
ldmud-3.2.9/src/util/xerq/lpc/
ldmud-3.2.9/src/util/xerq/lpc/www/
/*---------------------------------------------------------------------------
 * Pattern Parser v 2.0  (compat mode)
 * (C) Copyright 1990 JnA (jna@cd.chalmers.se)
 *
 *---------------------------------------------------------------------------
 * TODO: Put this efun in the USE_DEPRECATED lot.
 * 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.
 * If <env> is 0, environment(this_player()) is used as default.
 *
 * 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
 *    %l    : A single living 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.
 *    %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
 *    %l : returns a living
 *    %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.
 *
 * %i 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. int id (string txt)
 *       txt is an object name of the form "adj1 adj2 ... name".
 *       The function has to return non-zero if txt is a valid (singular) name
 *       for this particular object.
 *
 *   2. int plural_id (string txt)
 *       txt is an object name of the form "adj1 adj2 ... name".
 *       The function has to return non-zero if txt is a valid plural name
 *       for this particular object.
 *
 *   3. string adjectiv_id()
 *       When parsing commands like "get all red ones", the result from this
 *       function is used to construct the text passed to id(); in this
 *       example "red <adjectiv_id>". If this function doesn't exist,
 *       the last word from the result of short() is used instead.
 *
 * TODO: This module uses rather clumsy programming.
 * TODO: Comments are still sparse.
 *---------------------------------------------------------------------------
 */

#include "driver.h"

#if defined(SUPPLY_PARSE_COMMAND)

#include "typedefs.h"

#include <stdio.h>
#include <ctype.h>
#include <time.h>

#define NO_REF_STRING
#include "parse.h"

#include "actions.h"
#include "array.h"
#include "closure.h"
#include "interpret.h"
#include "gcollect.h"
#include "main.h"
#include "object.h"
#include "random.h"
#include "simulate.h"
#include "stdstrings.h"
#include "stralloc.h"
#include "svalue.h"
#include "wiz_list.h"
#include "xalloc.h"

/*-------------------------------------------------------------------------*/
/* Some useful macros
 */
#define EQ(x,y)  (strcmp(x,y)==0)
#define EQN(x,y) (strncmp(x,y,strlen(x))==0)
#define EMPTY(x) (x[0] == '\0')

#define KLUDGELEN 200  /* Size of the string buffers */

#define LVALUE svalue_t

/*-------------------------------------------------------------------------*/

typedef struct altern_objects_s altern_objects_t;

struct altern_objects_s
{
  altern_objects_t *next;
  object_t *ao_obj;
};

/* Type of an element from the parse pattern string.
 */
enum ptype_e
  { EP = 0  /* End Parse marker */
  , SI      /* %o single item */
  , IT      /* %i  items */
  , US      /* %l  single living object */
  , PP      /* %p  prepositions */
  , TX      /* %s  string of words */
  , DTX     /* 'word' */
  , OTX     /* [word] */
  , ALT     /* /   alternates */
  , W1      /* %w  string of one word */
  , NUM     /* %d  integer */
  };

typedef enum ptype_e ptype_t;

/*-------------------------------------------------------------------------*/

static char gMword[KLUDGELEN];
  /* Text inside '' or [].
   */

static char gFword[KLUDGELEN];
  /* Temp word gotten by getfirst().
   */

static altern_objects_t *gOblist;
  /* List of accessible objects.
   */

static char gAdjective[4*KLUDGELEN];
  /* all adjectives before objname
   */

static LVALUE *gCarg;
  /* Current argument to %_
   */

static int gWantnum;
  /* Number of wanted items 0 = all
   */

static altern_objects_t *gPobjects;
  /* List of parsed objects
   */

static LVALUE *gTxarg;
  /* Argument of LPCvariable to store %s
   */

static LVALUE *gForprepos;
  /* Save arg* here for findprepos
   */

static LVALUE *gTopStack;
  /* arg* to arg after my last
   */

static svalue_t sv_tmp;
  /* Buffer for function results.
   */

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 object_t *
find_living_object (char *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.
 */

{
    static char *function_names[2] = { "find_living", "find_player"};

    svalue_t *sp, *svp;

    sp = inter_sp;
    sp++;

    /* 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 */
        put_string(sp, make_shared_string(function_names[player ? 1 : 0]));
        if (!sp->u.string)
            error("(old_parse_command) Out of memory (%lu bytes) for string\n"
                 , (unsigned long)strlen(function_names[player ? 1 : 0]));
        inter_sp = sp;
        symbol_efun(sp);
        *svp = *sp;
        inter_sp = sp - 1;
    }

    /* Call the closure */
    put_string(sp, make_shared_string(name));
    if ( !sp->u.string)
        error("(old_parse_command) Out of memory (%lu bytes) for result\n"
             , (unsigned long)strlen(name));
    inter_sp = sp;
    call_lambda(svp, 1);
    pop_stack();

    return sp->type != T_OBJECT ? NULL : sp->u.ob;
} /* find_living_object() */

/*-------------------------------------------------------------------------*/

#ifdef GC_SUPPORT

void
clear_old_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_old_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 */

/*-------------------------------------------------------------------------*/
static char *
backstrchr (char *apa, char ch)

/* Search for character <ch> in string <apa> and return a pointer to
 * the following character, or NULL if not found.
 */

{
    char *sp;

    sp = strrchr(apa, ch);
    return sp ? sp+1 : NULL;
} /* backstrchr() */

/*-------------------------------------------------------------------------*/
static char *
lowercase (char *apa)

/* Convert string <apa> to lowercase in-place and also return it.
 */

{
    char *bepa;

    bepa = apa;
    while (*bepa)
    {
        if (*bepa > 'A' && *bepa <= 'Z')
            *bepa += 'a'-'A';
        bepa++;
    }
    return apa;
} /* lowercase() */

/*-------------------------------------------------------------------------*/
static void
fixlist (object_t *first, altern_objects_t **parent, int *antal)

/* Add all objects in the inventory of <first> (including itself) to
 * the list <parent>. <antal> holds the number of elements in the parent
 * list.
 */

{
    object_t *cur;
    altern_objects_t *this, *taill;

    cur = first;
    this = *parent;
    if (!cur)
        return;
    while (cur)
    {
        if (cur->contains)
            fixlist(cur->contains, &this, antal);
        taill = this;
        this = xalloc(sizeof(*this));
        this->ao_obj = ref_object(cur, "parse->fixlist");
        (*antal)++;
        this->next = taill;
        cur = cur->next_inv;
    }
    *parent = this;
} /* fixlist() */

/*-------------------------------------------------------------------------*/
static int
makeobjlist (altern_objects_t **alist, object_t *src)

/* Create a list of the theoretically accessible objects from <src>
 * and store it in *<alist>.
 * If <src> is NULL; environment(command_giver) is used.
 * Result is the number of objects found.
 */

{
    object_t *env, *cur;
    altern_objects_t *this;
    int cnt;

    *alist = NULL;

    if (!src)
        env = command_giver->super;
    else
        env = src;
    if (!env)
        return 0;
    if (!(env->contains))
        return 0;

    cur = env->contains;
    this = NULL;
    cnt = 0;
    fixlist(cur, &this, &cnt);
    *alist = this;
    return cnt;
} /* makeobjlist() */

/*-------------------------------------------------------------------------*/
static int
itnumalt (void)

/* Return the number of objects in the gPobjects list.
 */

{
    int ant;
    altern_objects_t *ao;

    ant = 0;
    ao = gPobjects;
    while (ao)
    {
        ant++;
        ao = ao->next;
    }
    return ant;
} /* itnumalt() */

/*-------------------------------------------------------------------------*/
static object_t *
italt (int a)

/* Return a pointer to the <a>th object in the gPobjects list.
 */

{
    int ant;
    altern_objects_t *ao;

    ant = 0;
    ao = gPobjects;
    while (ao)
    {
        ant++;
        if (ant == a)
            return ao->ao_obj;
        ao = ao->next;
    }

    return NULL;
} /* italt() */

/*-------------------------------------------------------------------------*/
static void
italt_new (void)

/* Free the list gPobjects.
 */

{
    altern_objects_t *ao;
    altern_objects_t *ao2;

    ao2 = ao = gPobjects;
    gPobjects = NULL;
    while (ao)
    {
        ao = ao->next;
        free_object(ao2->ao_obj, "parse->italt_new()");
        xfree(ao2);
        ao2 = ao;
    }
} /* italt_new() */

/*-------------------------------------------------------------------------*/
static void
italt_loadall (void)

/* Create gPobjects as the list of all the theoretically accessible
 * objects.
 */

{
    if (gPobjects) italt_new();
    makeobjlist(&gPobjects,0);
} /* italt_loadall() */

/*-------------------------------------------------------------------------*/
static void
italt_put (object_t *obj)

/* Put object <obj> at the end of the list gPobjects.
 */

{
    altern_objects_t *ao, *old;

    ao = gPobjects;
    old = ao;
    while (ao)
    {
        old = ao;
        ao = ao->next;
    }

    ao = xalloc(sizeof(*ao));
    if (old)
        old->next = ao;
    else
        gPobjects = ao;

    ao->ao_obj = ref_object(obj, "parse->italt_put()");
    ao->next = NULL;
} /* italt_put() */

/*-------------------------------------------------------------------------*/
static char *
getfirst (char **cmd)

/* Put the first word of <cmd> into gFword and point <cmd> to the
 * next word. Result is gFword.
 */

{
    size_t pos;
    Bool inqoute;
    char ch, *st;

    st = *cmd;
    strcpy(gFword,"");
    if (st[0] == '\0')
        return gFword;

    ch = ' ';
    pos = 0;
    inqoute = MY_FALSE;
    while (st[0] <= ' ' && st[0]) /* Skip leading spaces */
        st++;

    if (st[0] == '\'' || st[0] == '[')
    {
        inqoute=MY_TRUE;
        ch = st[0];
        gFword[0] = st[0];
        pos=1;
        st++;
    }

    if (ch == '[')
        ch = ']';

    while (pos < KLUDGELEN-1 && st[0] && st[0] != ch)
    {
        gFword[pos++] = st[0];
        st++;
        if (st[0] && st[0] < ' ')  /* Replace ctrl chars */
            st[0]=' ';
    }

    if (inqoute && pos < KLUDGELEN-1)
    {
        gFword[pos++] = ch;
        if (st[0])
            st++;
    }

    gFword[pos] = '\0';
    *cmd = st;

    return gFword;
} /* getfirst() */

/*-------------------------------------------------------------------------*/
static char *
lookfirst (char *cmd)

/* Put first word of <cmd> into gFword without incrementing the callers
 * <cmd> pointer. Result is gFword.
 */

{
    return getfirst(&cmd);
} /* lookfirst() */

/*-------------------------------------------------------------------------*/
static int
call_obj (char *fnamn, object_t *on, char *apa)

/* Call the lfun <on>:<fnamn>(<apa>). If the result is a number, return it,
 * otherwise return 0.
 */

{
    svalue_t *ret;

    push_volatile_string(apa);
    ret = apply(fnamn, on, 1);
    if (!ret) return 0;
    if ( ret->type == T_NUMBER) return ret->u.number;
    return 0;
} /* call_obj() */

/*-------------------------------------------------------------------------*/
static char *
singfix (char *str)

/* Change the noun <str> from pluralform to singularform.
 * Result is a pointer to a static buffer or to a constant string.
 */

{
    static char sing[KLUDGELEN];
    char *sp;
    int sl;

    sl = strlen(str);
    if (sl < 2)
        return str;

    sp = &str[sl-3];
    strcpy(sing,str);
    sing[sl-3] = '\0';

    if (EQ(str,"corpses")) return "corpse";
    if (EQ(sp,"ses")) return strcat(sing,"s");
    else if (EQ(sp,"xes")) return strcat(sing,"x");
    else if (EQ(sp,"hes")) return strcat(sing,"h");
    else if (EQ(sp,"ies")) return strcat(sing,"y");
    else if (EQ(sp,"ves")) return strcat(sing,"fe");
    else if (sp[2]=='s') {
        strcat(sing,sp); sing[sl-1]=0;
        return sing;
    }

    if (EQ(str,"teeth")) return "tooth";
    if (EQ(str,"feet")) return "foot";
    if (EQ(str,"men")) return "man";
    if (EQ(str,"women")) return "woman";
    if (EQ(str,"children")) return "child";
    if (EQ(str,"sheep")) return "sheep";

    return str;
} /* singfix() */

/*-------------------------------------------------------------------------*/
static int
matchadjective (char *adjs)

/* Check with objs in gOblist if accepts all adjectives in <adjs>, and return
 * the number of objects which do.
 *
 * This is used when the name of the object is not know, ie in cmds
 * like 'get all the red ones'.
 *
 * If the function STR_PC_ADJ_ID doesn't exist in the objects, the last word
 * from STR_PC_SHORT is used as object name and set as suffix to the adjectives
 * in calls to id().
 */

{
    char ad[KLUDGELEN], tot[2*KLUDGELEN], *sp, *sp2;
    altern_objects_t *ao;
    object_t *on;
    svalue_t *ret;

    /* Loop over the gOblist */
    for (ao = gOblist; ao; ao=ao->next)
    {
        on = ao->ao_obj;
        sp = adjs;
        while (on && sp && *sp)
        {
            while (*sp == ' ')
                sp++;
            sp2 = strchr(sp,' ');
            if (sp2)
            {
                *sp2 = '\0';
                strcpy(ad, sp);
                *sp2 = ' ';
                sp = sp2;
                if (!call_obj(STR_PC_ADJ_ID, on, ad))
                    on = NULL; /* Not ok */
            }
            sp = sp2;
        }

        if (on)
            italt_put(on);
        else
        {
            on = ao->ao_obj;
            ret = apply(STR_PC_SHORT,on,0);
            if (ret && ret->type==T_STRING)
            {
                sp = ret->u.string;
                sp2 = backstrchr(sp,' ');
                if (sp2)
                    sp = sp2;
                sprintf(tot, "%s%s", adjs, sp);
                lowercase(tot);
                if (!call_obj(STR_ID,on,tot))
                    on = NULL;
            }
            else
                on = NULL;
        }
        if (on)
            italt_put(on);
    }

    return itnumalt();
} /* matchadjective() */

/*-------------------------------------------------------------------------*/
static Bool
check_for_general (char *onam, Bool plur)

/* Return true if the word <oname> is one of the general words like 'those'.
 */
{

    static char* plurpron[] = {"those","them","these","ones","$"};
    static char* singpron[] = {"this","one","it","$"};
    size_t ilop;

    if (EMPTY(onam))
        return MY_FALSE;

    if (plur)
    {
        for (ilop = 0; !EQ(plurpron[ilop],"$"); ilop++)
            if (EQ(plurpron[ilop], onam))
                return MY_TRUE;
    }
    else
    {
        for (ilop = 0; !EQ(singpron[ilop],"$"); ilop++)
            if (EQ(singpron[ilop], onam))
                return MY_TRUE;
    }

    return MY_FALSE;
} /* check_for_general() */

/*-------------------------------------------------------------------------*/
static int
order_num (char *wd)

/* Parse a (positive) ordinal (incl. zero) from the command <wd> and return it.
 * If there is no ordinal, return -1.
 *
 * The function recognizes digits, and numbers expressed in words.
 */

{
    static char *onums[] = {"first","second","third","fourth","fifth","sixth",
                    "seventh","eighth","nineth","tenth",
                    "eleventh","twelfth","thirteenth","fourteenth","fifteenth","sixteenth",
                    "seventeenth","eighteenth","nineteenth","dummy"};
    static char *onumt[] = {"twenty","thirty","forty","fifty","sixty","seventy",
                    "eighty","ninety"};
    static char *onumta[] = {"twentieth","thirtieth","fortieth","fiftieth","sixtieth","seventieth",
                    "eightieth","ninetieth"};

    char ns[KLUDGELEN];

    int ilop, nm;

    if (EMPTY(wd))
        return -1;

    /* Test for simple textual numbers */
    for (ilop = 1; ilop < 20; ilop++)
    {
        if (EQ(onums[ilop-1],wd))
            return ilop;
    }

    /* Test for composite textual numbers */
    /* TODO: This could be done better */
    for (nm = 0; nm < 8; nm++)
        for (ilop = 0; ilop < 9; ilop++)
        {
            if (ilop > 0)
            {
                sprintf(ns, "%s%s", onumt[nm], onums[ilop-1]);
                if (EQ(ns,wd))
                    return 20+nm*10+(ilop);
            }
            else if (EQ(onumta[nm],wd))
                return 20+nm*10+(ilop);
        }

    return -1;
} /* order_num() */

/*-------------------------------------------------------------------------*/
static int
numeric (char *wd)

/* Parse a positive number (incl. zero) from the command <wd> and return it.
 * If there is no number, return -1.
 *
 * The function recognizes digits, and numbers expressed in words.
 */

{
    static char *nums[] = {"one","two","three","four","five","six",
                    "seven","eight","nine","ten",
                    "eleven","twelve","thirteen","fourteen","fifteen","sixteen",
                    "seventeen","eighteen","nineteen"};
    static char *numt[] = {"twenty","thirty","forty","fifty","sixty","seventy",
                    "eighty","ninety"};

    char ns[KLUDGELEN];

    int ilop, nm;

    if (EMPTY(wd))
        return -1;

    /* Test for digit numerics */
    if (sscanf(wd, "%d", &nm))
        return (nm>=0) ? nm : -1;

    /* Test for simple textual numbers */
    if (EQ(wd,"a") || EQ(wd,"an"))
        return 1;

    for (ilop = 1; ilop < 20; ilop++)
        if (EQ(nums[ilop-1], wd))
            return ilop;

    /* Test for composite textual numbers */
    /* TODO: This could be done better */
    for (nm = 0; nm < 8; nm++)
        for (ilop = 0; ilop < 9; ilop++)
        {
            if (ilop > 0)
            {
                sprintf(ns,"%s%s",numt[nm],nums[ilop-1]);
                if (EQ(ns,wd))
                    return 20+nm*10+(ilop);
            }
            else if (EQ(numt[nm], wd))
                return 20+nm*10+(ilop);
        }

    return -1;
} /* numeric() */

/*-------------------------------------------------------------------------*/
static object_t *
matchobject2 (char ** cmd, Bool plur)

/* Search commandstring for "adj1 adj2 ... adjN objectname" and matching
 * object. If found, return the object and store the matched adjectives
 * in gAdjective as "adj1 adj2...".
 */

{
    object_t *on;
    altern_objects_t *ao;
    char *st, *ocmd, totname[2*KLUDGELEN], tot2[2*KLUDGELEN];

    ocmd = *cmd;
    strcpy(gAdjective, "");

    /* Loop over the words in cmd, catenating them in gAdjective,
     * until a complete id (adjectives and name) is complete.
     */
    st = lowercase(getfirst(cmd));
    while (st[0])
    {
        if (check_for_general(st, plur)
         && matchadjective (gAdjective)
           )
            return italt(1);

        sprintf(totname, "%s%s", gAdjective, st);
        if (plur)
            sprintf(tot2,"%s%s", gAdjective, singfix(st));

        /* Loop over the object list with the id so far */
        for (ao = gOblist; ao; ao = ao->next)
        {
            on = NULL;
            if (plur)
            {
                if (call_obj(STR_PC_PLURAL_ID, ao->ao_obj, totname))
                    on = ao->ao_obj;
                else if (call_obj(STR_ID, ao->ao_obj, tot2))
                    on = ao->ao_obj;
            }
            else if (call_obj(STR_ID, ao->ao_obj, totname))
                on = ao->ao_obj;
            if (on)
                italt_put(on);
        } /* for (ao) */

        if (!itnumalt())
        {
            strcat(gAdjective, st);
            strcat(gAdjective, " ");
            st = lowercase(getfirst(cmd));
        }
        else
            st[0] = '\0'; /* also breaks the loop */
    } /* while(st[0]) */

    if (itnumalt())
        return italt(1);

    *cmd = ocmd;
    return NULL;
} /* matchobject2() */

/*-------------------------------------------------------------------------*/
static object_t *
finditem (char **cmd, Bool plur)

/* Search the command for a valid object description and return the
 * object, if found.
 * The function calls itself recursively for plurals.
 */

{
    int nm;
    object_t *pn;
    char *ocmd, w1[KLUDGELEN];

    ocmd = *cmd;
    strcpy(w1, lowercase(getfirst(cmd)));
    italt_new(); /* Clear alternate list */
    gWantnum = 1; /* Parsed number of desired objects */

    if (EMPTY(w1))
    {
        *cmd = ocmd;
        return NULL;
    }

    /* Must be hardcode skipped for recursive with plural
     */
    if (EQ("the", w1))
    {
        *cmd = ocmd;
        getfirst(cmd);
        if (NULL != (pn = finditem(cmd,plur)))
            return pn;
        else
        {
            *cmd=ocmd;
            return NULL;
        }
    }

    /* Check things like: some <objname>
     */
    else if (EQ("some",w1) && !plur)
    {
        *cmd = ocmd;
        getfirst(cmd);
        /* Skip of in : some of the bottles */
        if (EQ(lowercase(lookfirst(*cmd)),"of"))
            getfirst(cmd);

        nm = random_number(6) + 2; /* "some" means random 2..7 */
        if (NULL != (pn = finditem(cmd, MY_TRUE)))
        {
            gWantnum=nm;
            return pn;
        }
    }

    /* Check things like: three <objname>
     */
    else if (!plur && (nm = numeric(w1)) > 0)
    {
        *cmd = ocmd;
        getfirst(cmd);
        /* Skip of in : two of the bottles */
        if (EQ(lowercase(lookfirst(*cmd)),"of"))
            getfirst(cmd);
        if (NULL != (pn = finditem(cmd, (nm != 1))))
        {
            gWantnum=nm;
            return pn;
        }
    }

    /* Check things like: third <objname>
     */
    else if (!plur && (nm = order_num(w1)) > 0)
    {
        *cmd = ocmd;
        getfirst(cmd);
        /* Skip of in : second of the bottles */
        if (EQ(lowercase(lookfirst(*cmd)),"of"))
        {
            getfirst(cmd);
            if (NULL != (pn = finditem(cmd,MY_TRUE)))
            {
                /* Fix second of the bottles */
                gWantnum = -nm;
                return pn;
            }
        }
        else
        {
            if (NULL != (pn = finditem(cmd,MY_FALSE)))
            {
                /* Fix second bottle */
                gWantnum = -nm;
                return pn;
            }
        }
    }


    /* Check things like: all <objname>
     */
    else if (EQ("all",w1) && !plur)
    {
        *cmd = ocmd;
        getfirst(cmd);
        /* Skip of in : all of the bottles */
        if (EQ(lowercase(lookfirst(*cmd)), "of"))
        {
            getfirst(cmd);
        }
        if (NULL != (pn = finditem(cmd,MY_TRUE)))
        {
            /* Handle: get all apples */
            gWantnum = 0; /* 0 means 'all' */
            return pn;
        }
        else
        {
            /* Handle: get all */
            italt_loadall();
            pn = italt(1);
            gWantnum = 0; /* 0 means 'all' */
        }
    }

    /* Search for: adj1 adj2 ... adjN objectname
     */
    else
    {
        *cmd = ocmd;
        if (plur)
            pn = matchobject2(cmd, plur);
        else
        {
            /* Standard singular */
            pn = matchobject2(cmd, MY_FALSE);
            if (!pn)
            {
                /* Handle things of type: get apples */
                pn = matchobject2(cmd, MY_TRUE);
                if (pn)
                    gWantnum = 0; /* Default this to all */
            }
        }
    }

    if (!pn)
        *cmd = ocmd;

    return pn;
} /* finditem() */

/*-------------------------------------------------------------------------*/
static Bool
findobject (char **cmd)

/* Find match in command <cmd> for %i in pattern.
 * If found, store it in gCarg and return TRUE.
 */

{
    int nm,s;
    object_t *ob;

    if (finditem(cmd, MY_FALSE))
    {
        if (gCarg)
        {
            vector_t *p;

            nm = itnumalt();
            p = allocate_array(nm+1);
            put_number(p->item, gWantnum);

            /* Make array in reverse order from italt() because
             * makeobjlist() has reversed the order on entry
             */
            for (s = 1; s <= nm; s++)
            {
                ob = italt(nm+1-s);
                if (ob)
                    put_ref_object(p->item+s, ob, "old_parse_command");
            }
            put_array(&sv_tmp, p);
            transfer_svalue(gCarg->u.lvalue, &sv_tmp);
        }
        return MY_TRUE;
    }

    return MY_FALSE;
} /* findobject() */

/*-------------------------------------------------------------------------*/
static object_t *
findplay (char **cmd)

/* Find match in command <cmd> for %l in pattern and return it.
 * If found, also store it in gCarg.
 */

{
    object_t *pn;
    char w1[KLUDGELEN];

    strcpy(w1, lowercase(lookfirst(*cmd)));
    /* can be fixed later with call to LPC command_giver->query_real_name()
     * if (EQ(w1,"me")) strcpy(w1,getmyname_jp());
     * if (EQ(w1,"myself")) strcpy(w1,getmyname_jp());
     */
    pn = find_living_object(w1, MY_TRUE);  /* Find player by name */
    if (pn)
    {
        getfirst(cmd);
        if (gCarg)
        {
            put_ref_object(&sv_tmp, pn, "old_parse_command(%l)");
            transfer_svalue(gCarg->u.lvalue, &sv_tmp);
        }
    }

    return pn;
} /* findplay() */

/*-------------------------------------------------------------------------*/
static Bool
findword (char **cmd, svalue_t *v)

/* Find match in command <cmd> for %p in the wordlist <v>.
 * If found, store it in gCarg and return TRUE.
 */

{
    char *w;
    vector_t *p;
    svalue_t sv;
    size_t cnt, m;
    int f;

    w = lookfirst(*cmd); lowercase(w);
    p = v->u.vec;
    f = -1;
    for (cnt = 0, m = VEC_SIZE(p); cnt < m; cnt++)
    {
        if (p->item[cnt].type == T_STRING
         && strcmp(p->item[cnt].u.string,w) == 0)
        {
            f=cnt; cnt=m;
        }
    }

    if (f < 0)
        return MY_FALSE;

    getfirst(cmd);
    if (!f)
        return MY_TRUE; /* Match and word in first element */

    /* Swap element 0 and f in array */
    sv = p->item[0];
    p->item[0] = p->item[f];
    p->item[f] = sv;

    return MY_TRUE;
} /* findword() */

/*-------------------------------------------------------------------------*/
static Bool
findprepos (char **cmd)

/* Find match in command <cmd> for %p in pattern.
 * If found, store it in gCarg and return TRUE.
 */

{
    static char *hard_prep[] = { "in", "from", "on", "under", "behind", "of"
                               , "for", "to", "with", "at", "off", "out"
                               , "down", "up", "around", "over", "into"
                               , "about", "inside"
                               , 0 };

    char *w;
    svalue_t *v;
    size_t cnt;

    if (gForprepos)
    {
        v = gForprepos->u.lvalue;
        if (v && v->type == T_POINTER)
            return findword(cmd, v);
    }

    /* No wordlist sent, use hard coded prepositions and return a string
     */
    w = lookfirst(*cmd);
    lowercase(w);
    for (cnt = 0; hard_prep[cnt]; cnt++)
    {
        if (strcmp(w, hard_prep[cnt]) == 0)
        {
            getfirst(cmd); /* Skip this word */
            if (gCarg)
            {
                put_malloced_string(&sv_tmp, string_copy(w));
                transfer_svalue(gCarg->u.lvalue, &sv_tmp);
                return MY_TRUE;
            }
        }
    }

    return MY_FALSE;
} /* findprepos() */

/*-------------------------------------------------------------------------*/
static Bool
findsingle (char **cmd)

/* Find match in command <cmd> for %o in pattern.
 * If found, store it in gCarg and return TRUE.
 */

{
    if (finditem(cmd, MY_FALSE))
    {
        if (itnumalt() == 1 && gCarg)
        {
            put_ref_object(&sv_tmp, italt(1), "old_parse_command(%o)" );
            transfer_svalue(gCarg->u.lvalue, &sv_tmp);
        }
        return MY_TRUE;
    }
    return MY_FALSE;
} /* findsingle() */

/*-------------------------------------------------------------------------*/
static ptype_t
get1ps (char **parsep, LVALUE **lin, Bool skip)

/* Get the first parsetype from the pattern <parsep> and, if not <skip>,
 * set <lin>.
 */

{
    char *cod, ch;
    ptype_t pt;
    LVALUE *l;

    pt = EP;
    cod = getfirst(parsep);
    l = *lin;

    /* Determine the ptype for the next parse pattern */
    ch = cod[0];
    if (ch == '%')
        ch = cod[1];

    switch (ch)
    {
    case 'i': case 'I': pt = IT; break;
    case 'l': case 'L': pt = US; break;
    case 's': case 'S': pt = TX; break;
    case 'w': case 'W': pt = W1; break;
    case 'o': case 'O': pt = SI; break;
    case 'p': case 'P': pt = PP; break;
    case 'd': case 'D': pt = NUM; break;
    case '\'': sscanf(cod,"'%[^\']",gMword); pt = DTX; break;
#if 0
    case '[': sscanf(cod,"\[%[^\135]",gMword); pt=OTX; /* 135 is oct for ] */
#else
    case '[': sscanf(cod,"\\[%[^]]",gMword); pt=OTX;
#endif
        if (gMword[strlen(gMword)-1]==']') gMword[strlen(gMword)-1]=0;
        break;
    case '/': pt = ALT; break;
    }

    gCarg = NULL;

    if (skip || pt == DTX || pt == OTX || pt == ALT)
        return pt;

    /* Pattern takes a variable as argument */
    if (l != gTopStack)
    {
        gForprepos = l;
        gCarg = l;
        l++;
    }
    else
    {
        gCarg = NULL;
        gForprepos = NULL;
    }

    *lin = l;
    return pt;
} /* get1ps() */

/*-------------------------------------------------------------------------*/
static void
addword (char *d, char *s)

/* Add " <s>" to <d>.
 * If gTxarg is set, a copy of the result is stored there.
 */

{
    if (strlen(d) + strlen(s) < KLUDGELEN-2)
    {
        if (!EMPTY(d))
            strcat(d, " ");
        strcat(d,s);
    }
    if (gTxarg)
    {
        put_malloced_string(&sv_tmp, string_copy(d));
        transfer_svalue(gTxarg->u.lvalue, &sv_tmp);
    }
} /* addword() */

/*-------------------------------------------------------------------------*/
Bool
e_old_parse_command ( char     *cs           /* Command to parse */
                , svalue_t *ob_or_array  /* Object or array of objects */
                , char     *ps           /* Special parsing pattern */
                , svalue_t *dest_args    /* Pointer to lvalue args on stack */
                , int       num_arg      /* Number of lvalues on stack */
                )

/* EFUN parse_command()
 *
 * This function implements the old parse_command() efun, called from
 * interpret.c.  Result is TRUE on success, and FALSE otherwise.
 */

{
    int     altflag;
    int     txflag;
    ptype_t ptyp;     /* Parsetype: %o %i %l %p %s 'word' [word] / %w */

    char *parsep;                /* Parsepattern */
    char *cmd;                   /* Command to be parsed */
    char *ops,*ocs;              /* Temporary parse and command */
    char tx_jp[KLUDGELEN];       /* Fill up string for %s */
    char *tx_save_parsep = NULL; /* Where to continue parsing when a word was added
                                  * to a string
                                  */
    char *tx_end_first_pattern = NULL;
    LVALUE *l;                   /* Argument pointer in dest_args */

    /* No need to parse against destructed objects */
    if (ob_or_array->type == T_OBJECT
     && (ob_or_array->u.ob->flags & O_DESTRUCTED))
        return MY_FALSE;

    /* In mudlib 3.0 we will not have this. */
    if (cs[0] == '@')
        error("Unsupported @ construct.\n");

    ocs = cmd = string_copy(cs);

    /* Get the accessible objects
     */
    if (ob_or_array->type == T_OBJECT)
        makeobjlist(&gOblist, ob_or_array->u.ob);
    else
    {
        /* Copy list of given accessible objects in reverse order */
        vector_t *v;
        svalue_t *vv;
        size_t cnt;

        v = ob_or_array->u.vec;
        italt_new();
        for (cnt = 0; cnt < VEC_SIZE(v); cnt++)
        {
          vv = &(v->item[VEC_SIZE(v)-1-cnt]);
          if (vv->type == T_OBJECT)
              italt_put(vv->u.ob);
        }
        gOblist = gPobjects;
        gPobjects = NULL;
    }

    ops = parsep = string_copy(ps);
    gPobjects = NULL;

    /* Start parsing
     */
    txflag = -1;
    gCarg = NULL;
    gTxarg = NULL;
    gTopStack = dest_args + num_arg;
    l = dest_args;

    /* Parse loop */
    ptyp = get1ps(&parsep, &l, MY_FALSE); /* Get first pattern from parse */
    while (ptyp != EP)
    {
        altflag = 0;
        switch(ptyp) /* See which pattern type to search for */
        {
        case EP: break;

        case SI: if (findsingle(&cmd)) altflag = 1; /* %o */ break;
        case IT: if (findobject(&cmd)) altflag = 1; /* %i */ break;
        case US: if (findplay(&cmd))   altflag = 1; /* %l */ break;
        case PP: if (findprepos(&cmd)) altflag = 1; /* %p */ break;

        case TX: /* %s */
            txflag = 1;
            strcpy(tx_jp,"");
            gTxarg = gCarg;
            altflag = 1;
            tx_save_parsep = parsep;
            break;

        case DTX: /* 'word' */
            if (EQ(lowercase(gMword),lowercase(lookfirst(cmd))))
            {
                getfirst(&cmd);
                altflag = 1;
            }
            break;

        case OTX: /* [word] */
            if (EQ(lowercase(gMword),lowercase(lookfirst(cmd))))
            {
                getfirst(&cmd);
            }
            altflag = 1;
                /* Always take next parsepattern type [word] is opt */
            break;

        case ALT: /* / */
            altflag = 1; /* Should not be found here, if so skip it */
            break;

        case W1: /* %w */
            gTxarg = gCarg;
            strcpy(tx_jp,"");
            altflag = 1;
            addword(tx_jp,getfirst(&cmd));
            break;

        case NUM: /* %d */
            if ((altflag = numeric(lookfirst(cmd))) > 0)
            {
                if (gCarg)
                {
                    put_number(&sv_tmp, altflag);
                    transfer_svalue(gCarg->u.lvalue, &sv_tmp);
                }
                altflag = 1;
                getfirst(&cmd);
            }
            break;
        } /* switch(ptyp) */

        /* Pattern checked altflag==1 indicates match
        */
        if (altflag) /* Pattern matched, fetch next and end string input */
        {
            if (ptyp == TX)
            {
                ptyp = get1ps(&parsep, &l, MY_FALSE);
                tx_end_first_pattern = parsep;
            }
            else
            {
                ptyp = get1ps(&parsep, &l, MY_FALSE);
                txflag = -1; /* End string input if not just started */
            }
            while (ptyp == ALT) /* Skip left over alternatives */
            {
                ptyp = get1ps(&parsep, &l, MY_TRUE);  /* Skip this pattern */
                ptyp = get1ps(&parsep, &l, MY_FALSE); /* Next real pattern or ALT */
            }
        }
        else /* Pattern did not match */
        {
            char *a;
            LVALUE *try;
            ptype_t tmp;

            a = parsep;
            try = l;
            tmp = get1ps(&a, &try, MY_FALSE);
            if (tmp == ALT)
            {
                parsep = a;                           /* Skip ALT */
                ptyp = get1ps(&parsep, &l, MY_FALSE); /* Next real pattern or ALT */
            }
            else
            {
                if (txflag >= 0 && *getfirst(&cmd))
                {
                    /* %s is defined, add word and try pattern again */
                    addword(tx_jp, gFword);
                    if (parsep != tx_end_first_pattern)
                    {
                        parsep = tx_save_parsep;
                        ptyp = get1ps(&parsep, &l, MY_FALSE);
                    }
                }
                else
                    break; /* Impossible to match pattern, exit */
            }
        }
    } /* while() */

    /* End of pattern reached, what have got? What is left?
    */
    if (txflag >= 0) /* It ended with a %s, fill up the string */
    {
        while (!EMPTY(cmd))
            addword(tx_jp,getfirst(&cmd));
    }

    if (!EMPTY(cmd))  /* No match so set ptyp != EP */
        ptyp = ALT;

    /* Now clean up our mess, no alloced mem should remain
     */
    xfree(ocs);
    xfree(ops);
    if (gPobjects)  /* Free alternate object list */
        italt_new();
    if (gOblist)
    {
        /* Free list of accessible objects */
        gPobjects = gOblist;
        italt_new();
    }

    return (ptyp == EP);
} /* e_old_parse_command() */

#endif /* SUPPLY_PARSE_COMMAND */

/***************************************************************************/