home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / t / toaster.zip / Compat / parse.c < prev    next >
C/C++ Source or Header  |  1991-12-31  |  44KB  |  1,739 lines

  1. /*
  2.  
  3.   Pattern Parser package for LPmud, JnA 1991
  4.  
  5.   Ver 3.1
  6.  
  7.   If you have questions or complaints about this code please refer them
  8.   to jna@cd.chalmers.se
  9.  
  10. */
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14. #include <time.h>
  15. #include "lint.h"
  16. #include "interpret.h"
  17. #include "config.h"
  18. #include "object.h"
  19. #include "wiz_list.h"
  20.  
  21. extern char *string_copy PROT((char *)), *xalloc PROT((int));
  22. extern int d_flag; /* for debugging purposes */
  23. extern struct object *previous_ob;
  24.  
  25. #ifndef tolower            /* On some systems this is a function */
  26. extern int tolower PROT((int));
  27. #endif
  28.  
  29. /*****************************************************
  30.  
  31.   This is the parser used by the efun parse_command
  32.  
  33. */
  34. /*
  35.  
  36.   General documentation:
  37.  
  38.   parse_command() is one of the most complex efun in LPmud to use. It takes
  39.   some effort to learn and use, but when mastered, very powerfull constructs
  40.   can be implemented.
  41.  
  42.   Basically parse_command() is a piffed up sscanf operating on word basis. It
  43.   works similar to sscanf in that it takes a pattern and a variable set of
  44.   destination arguments. It is together with sscanf the only efun to use
  45.   pass by reference for other variables than arrays.
  46.  
  47.   To make the efun usefull it must have a certain support from the mudlib,
  48.   there is a set of functions that it needs to call to get relevant
  49.   information before it can parse in a sensible manner.
  50.  
  51.   In earlier versions it used the normal id() lfun in the LPC objects to
  52.   find out if a given object was identified by a certain string. This was
  53.   highly inefficient as it could result in hundreds or maybe thousands of
  54.   calls when very long commands were parsed. 
  55.   
  56.   The new version relies on the LPC objects to give it three lists of 'names'.
  57.  
  58.        1 - The normal singular names.
  59.        2 - The plural forms of the names.
  60.        3 - The acknowledged adjectives of the object.
  61.  
  62.   These are fetched by calls to the functions:
  63.  
  64.        1 - string *parse_command_id_list();
  65.        2 - string *parse_command_plural_id_list();
  66.        3 - string *parse_command_adjectiv_id_list();
  67.  
  68.   The only really needed list is the first. If the second does not exist
  69.   than the efun will try to create one from the singluar list. For 
  70.   grammatical reasons it does not always succeed in a perfect way. This is
  71.   especially true when the 'names' are not single words but phrases.
  72.  
  73.   The third is very nice to have because it makes constructs like
  74.   'get all the little blue ones' possible.
  75.  
  76.   Apart from these functions that should exist in all objects, and which
  77.   are therefore best put in /std/object.c there is also a set of functions
  78.   needed in /secure/master.c These are not absolutely necessary but they
  79.   give extra power to the efun.
  80.  
  81.   Basically these /secure/master.c lfuns are there to give default values
  82.   for the lists of names fetched from each object.
  83.  
  84.   The names in these lists are applicable to any and all objects, the first
  85.   three are identical to the lfun's in the objects:
  86.  
  87.        string *parse_command_id_list()
  88.                 - Would normally return: ({ "one", "thing" })
  89.  
  90.        string *parse_command_plural_id_list()
  91.                 - Would normally return: ({ "ones", "things", "them" })
  92.  
  93.        string *parse_command_adjectiv_id_list()
  94.                 - Would normally return ({ "iffish" })
  95.  
  96.   The last two are the default list of the prepositions and a single so called
  97.   'all' word. 
  98.  
  99.        string *parse_command_prepos_list()
  100.                  - Would normally return: ({ "in", "on", "under" })
  101.  
  102.        string parse_command_all_word()
  103.                  - Would normally return: "all"
  104.  
  105.   IF you want to use a different language than English but still want the
  106.   default pluralform maker to work, you need to replace parse.c with the
  107.   following file:
  108.  
  109. #if 0
  110.     * Language configured parse.c
  111.     *
  112.     #define PARSE_FOREIGN
  113.    
  114.     char *parse_to_plural(str)
  115.         char *str;
  116.     {
  117.  
  118.         * Your own plural converter for your language *
  119.      
  120.     }
  121.  
  122.       * The numberwords below should be replaced for the new language *
  123.  
  124.     static char *ord1[] = {"", "first", "second", "third", "fourth", "fifth",
  125.                "sixth", "seventh", "eighth", "nineth", "tenth",
  126.                "eleventh", "twelfth", "thirteenth", "fourteenth",
  127.                "fifteenth", "sixteenth", "seventeenth", 
  128.                "eighteenth","nineteenth"};
  129.  
  130.     static char *ord10[] = {"", "", "twenty","thirty","forty","fifty","sixty",
  131.                 "seventy", "eighty","ninety"};
  132.     
  133.     static char *sord10[] = {"", "", "twentieth", "thirtieth", "fortieth",
  134.                  "fiftieth", "sixtieth","seventieth", "eightieth",
  135.                  "ninetieth"};
  136.  
  137.     static char *num1[] = {"", "one","two","three","four","five","six",
  138.                "seven","eight","nine","ten",
  139.                "eleven","twelve","thirteen","fourteen","fifteen",
  140.                "sixteen", "seventeen","eighteen","nineteen"};
  141.  
  142.     static char *num10[] = {"", "", "twenty","thirty","forty","fifty","sixty",
  143.                "seventy", "eighty","ninety"};
  144.  
  145.     #include "parse_english.c"      * This parse.c file *
  146.  
  147. #endif
  148.   
  149.   When all these things are defined parse_command() works best and most
  150.   efficient. What follows is the docs for how to use it from LPC:
  151.  
  152.  
  153.   Doc for LPC function
  154.  
  155. int parse_command(string, object/object*, string, destargs...)
  156.  
  157.             Returns 1 if pattern matches
  158.  
  159.     string        Given command
  160.  
  161.     object*        if arr 
  162.     object            array holding the accessible objects
  163.             if ob
  164.                 object from which to recurse and create
  165.                 the list of accessible objects, normally
  166.                 ob = environment(this_player())
  167.     string        Parsepattern as list of words and formats:
  168.             Example string = " 'get' / 'take' %i "
  169.             Syntax:
  170.                 'word'         obligatory text
  171.                 [word]        optional text
  172.                 /        Alternative marker
  173.                 %o        Single item, object
  174.                 %l        Living objects
  175.                 %s        Any text
  176.                 %w              Any word
  177.                 %p        One of a list (prepositions)
  178.                 %i        Any items
  179.                 %d              Number 0- or tx(0-99)
  180.  
  181.     destargs    This is the list of result variables as in sscanf
  182.             One variable is needed for each %_
  183.             The return types of different %_ is:
  184.             %o    Returns an object
  185.             %s    Returns a string of words
  186.             %w      Returns a string of one word
  187.             %p    Can on entry hold a list of word in array
  188.                 or an empty variable
  189.                 Returns:
  190.                    if empty variable: a string
  191.                    if array: array[0]=matched word
  192.             %i    Returns a special array on the form:
  193.                 [0] = (int) +(wanted) -(order) 0(all)
  194.                 [1..n] (object) Objectpointers    
  195.             %l    Returns a special array on the form:
  196.                 [0] = (int) +(wanted) -(order) 0(all)
  197.                 [1..n] (object) Objectpointers
  198.                                 These are only living objects.
  199.             %d      Returns a number
  200.  
  201.   The only types of % that uses all the loaded information from the objects
  202.   are %i and %l. These are in fact identical except that %l filters out
  203.   all nonliving objects from the list of objects before trying to parse.
  204.  
  205.   The return values of %i and %l is also the most complex. They return an
  206.   array consisting of first a number and then all possible objects matching.
  207.   As the typical string matched by %i/%l looks like: 'three red roses',
  208.   'all nasty bugs' or 'second blue sword' the number indicates which 
  209.   of these numerical constructs was matched:
  210.  
  211.          if numeral >0 then three, four, five etc were matched
  212.          if numeral <0 then second, twentyfirst etc were matched
  213.          if numeral==0 then 'all' or a generic plural form such as 'apples'
  214.                             were matched.
  215.  
  216.   NOTE!
  217.        The efun makes no semantic implication on the given numeral. It does
  218.        not matter if 'all apples' or 'second apple' is given. A %i will
  219.        return ALL possible objects matching in the array. It is up to the
  220.        caller to decide what 'second' means in a given context.
  221.  
  222.        Also when given an object and not an explicit array of objects the
  223.        entire recursive inventory of the given object is searched. It is up
  224.        to the caller to decide which of the objects are actually visible
  225.        meaning that 'second' might not at all mean the second object in
  226.        the returned array of objects.
  227.             
  228. Example:
  229.  
  230.  if (parse_command("spray car",environment(this_player()),
  231.                       " 'spray' / 'paint' [paint] %i ",items))  
  232.  {
  233.       If the pattern matched then items holds a return array as described
  234.         under 'destargs' %i above.
  235.      
  236.  }
  237.  
  238.  BUGS / Features
  239. :
  240.  
  241.  Patterns of type: "%s %w %i"
  242.    Might not work as one would expect. %w will always succeed so the arg
  243.    corresponding to %s will always be empty.
  244.  
  245.  'all' does not work properly, it only works as a number. You have to do
  246.  'get all things' to match everything. (Will be fixed)
  247.  
  248. */ 
  249.  
  250. /* Some useful string macros
  251. */
  252. #define EQ(x,y) (strcmp(x,y)==0)
  253. #define EQN(x,y) (strncmp(x,y,strlen(x))==0)
  254. #define EMPTY(x) (strcmp(x,"")==0)
  255.  
  256.  
  257. /* Function in LPC which returns a list of ids 
  258. */
  259. #define QGET_ID "parse_command_id_list"        
  260.  
  261. /* Function in LPC which returns a list of plural ids 
  262. */
  263. #define QGET_PLURID "parse_command_plural_id_list"   
  264.  
  265. /* Function in LPC which returns a list of adjectiv ids 
  266. */
  267. #define QGET_ADJID "parse_command_adjectiv_id_list" 
  268.  
  269. /* Function in LPC which returns a list of prepositions
  270. */
  271. #define QGET_PREPOS "parse_command_prepos_list" 
  272.  
  273. /* Function in LPC which returns the 'all' word
  274. */
  275. #define QGET_ALLWORD "parse_command_all_word"
  276.  
  277. /* Global vectors for 'caching' of ids
  278.  
  279.    The main 'parse' routine stores these on call, making the entire
  280.    parse_command() reentrant.
  281. */
  282. static struct vector    *gId_list    = 0;
  283. static struct vector    *gPluid_list    = 0;
  284. static struct vector    *gAdjid_list    = 0;
  285. static struct vector    *gId_list_d    = 0;  /* From master */
  286. static struct vector    *gPluid_list_d    = 0;  /* From master */
  287. static struct vector    *gAdjid_list_d    = 0;  /* From master */
  288. static struct vector    *gPrepos_list    = 0;  /* From master */
  289. static char         *gAllword       = 0;  /* From master */
  290.  
  291. /*
  292.  * Function name:     load_lpc_info
  293.  * Description:        Loads relevant information from a given object.
  294.  *            This is the ids, plural ids and adjectiv ids. This
  295.  *            is the only calls to LPC objects other than the
  296.  *                      master object that occur within the efun
  297.  *                      parse_command().
  298.  * Arguments:        ix: Index in the array
  299.  *            ob: The object to call for information.
  300.  */
  301. void load_lpc_info(ix, ob)
  302.     int    ix;
  303.     struct object *ob;
  304. {
  305.     struct vector *tmp, *sing;
  306.     struct svalue sval, *ret;
  307.     int il, make_plural = 0;
  308.     char *str;
  309.     char *parse_to_plural();
  310.  
  311.     if (!ob)
  312.     return;
  313.  
  314.     if (gPluid_list && 
  315.     gPluid_list->size > ix && 
  316.     gPluid_list->item[ix].type == T_NUMBER &&
  317.     gPluid_list->item[ix].u.number == 0)
  318.     {
  319.     ret = apply(QGET_PLURID, ob, 0);
  320.     if (ret && ret->type == T_POINTER) 
  321.         assign_svalue_no_free(&gPluid_list->item[ix], ret);
  322.     else 
  323.     {
  324.         make_plural = 1;
  325.         gPluid_list->item[ix].u.number = 1;
  326.     }
  327.     }
  328.  
  329.     if (gId_list && 
  330.     gId_list->size > ix && 
  331.     gId_list->item[ix].type == T_NUMBER &&
  332.     gId_list->item[ix].u.number == 0)
  333.     {
  334.     ret = apply(QGET_ID, ob, 0);
  335.     if (ret && ret->type == T_POINTER)
  336.     {
  337.         assign_svalue_no_free(&gId_list->item[ix], ret);
  338.         if (make_plural)
  339.         {
  340.         tmp = allocate_array(ret->u.vec->size);
  341.         sing = ret->u.vec;
  342.         for (il = 0; il < tmp->size; il++)
  343.         {
  344.             if (sing->item[il].type == T_STRING)
  345.             {
  346.             str = parse_to_plural(sing->item[il].u.string);
  347.             sval.type = T_STRING;
  348.             sval.string_type = STRING_MALLOC;
  349.             sval.u.string = string_copy(str);
  350.             assign_svalue_no_free(&tmp->item[il],&sval);
  351.             }
  352.         }
  353.         sval.type = T_POINTER;
  354.         sval.u.vec = tmp;
  355.         assign_svalue_no_free(&gPluid_list->item[ix], &sval);
  356.         }
  357.     }
  358.     else
  359.     {
  360.         gId_list->item[ix].u.number = 1;
  361.     }
  362.     }
  363.  
  364.     if (gAdjid_list && 
  365.     gAdjid_list->size > ix &&     
  366.     gAdjid_list->item[ix].type == T_NUMBER &&
  367.     gAdjid_list->item[ix].u.number == 0)
  368.     {
  369.     ret = apply(QGET_ADJID, ob, 0);
  370.     if (ret && ret->type == T_POINTER)
  371.         assign_svalue_no_free(&gAdjid_list->item[ix], ret);
  372.     else
  373.         gAdjid_list->item[ix].u.number = 1;
  374.     }
  375. }
  376.  
  377. /* Main function, called from interpret.c
  378. */
  379.  
  380. /*
  381.  * Function name:     parse
  382.  * Description:        The main function for the efun: parse_command()
  383.  *            It parses a given command using a given pattern and
  384.  *            a set of objects (see args below). For details
  385.  *            see LPC documentation of the efun.
  386.  * Arguments:        cmd: The command to parse
  387.  *            ob_or_array: A list of objects or one object from 
  388.  *                         which to make a list of objects by
  389.  *                     using the objects deep_inventory
  390.  *            pattern: The given parse pattern somewhat like sscanf
  391.  *                     but with different %-codes, see efun docs.
  392.  *            stack_args: Pointer to destination arguments.
  393.  *            num_arg: Number of destination arguments.
  394.  * Returns:        True if command matched pattern.
  395.  */
  396. int parse (cmd, ob_or_array, pattern, stack_args, num_arg)
  397.     char         *cmd;              /* Command to parse */
  398.     struct svalue     *ob_or_array;      /* Object or array of objects */
  399.     char        *pattern;    /* Special parsing pattern */
  400.     struct svalue     *stack_args;    /* Pointer to lvalue args on stack */
  401.     int         num_arg;    /* Number of args on stack */
  402. {
  403.     struct vector    *obvec, *patvec, *wvec;
  404.     struct vector    *old_id, *old_plid, *old_adjid;
  405.     struct vector    *old_id_d, *old_plid_d, *old_adjid_d, *old_prepos;
  406.     char        *old_allword;     
  407.     int            pix, cix, six, fail, fword, ocix, fpix;
  408.     struct svalue    *pval;
  409.     void        check_for_destr();    /* In interpret.c */
  410.     struct svalue    *sub_parse();
  411.     struct svalue    *slice_words();
  412.     void        stack_put();
  413.     struct vector    *deep_inventory();
  414.  
  415.     if (ob_or_array->type == T_POINTER)
  416.         check_for_destr(ob_or_array->u.vec);
  417.  
  418.     wvec = explode_string(cmd," ");
  419.     patvec = explode_string(pattern," ");
  420.  
  421.     wvec->ref++;
  422.     patvec->ref++;
  423.  
  424.     if (ob_or_array->type == T_POINTER)
  425.     obvec = ob_or_array->u.vec;
  426.     else
  427.     obvec = deep_inventory(ob_or_array->u.ob, 1);
  428.  
  429.     check_for_destr(obvec);
  430.  
  431.     obvec->ref++;
  432.  
  433.     /* Copy and  make space for id arrays
  434.     */
  435.     old_id      = gId_list; 
  436.     old_plid    = gPluid_list; 
  437.     old_adjid   = gAdjid_list;
  438.     old_id_d      = gId_list_d; 
  439.     old_plid_d    = gPluid_list_d; 
  440.     old_adjid_d   = gAdjid_list_d;
  441.     old_prepos    = gPrepos_list;
  442.     old_allword   = gAllword;
  443.  
  444.     gId_list    = allocate_array(obvec->size);
  445.     gPluid_list  = allocate_array(obvec->size);
  446.     gAdjid_list = allocate_array(obvec->size);
  447.     
  448.     /* Get the default ids of 'general references' from master object
  449.     */
  450.     pval = apply_master_ob(QGET_ID,0);
  451.     if (pval && pval->type == T_POINTER)
  452.     {
  453.     gId_list_d = pval->u.vec;
  454.     pval->u.vec->ref++;
  455.     }
  456.     else
  457.     gId_list_d = 0;
  458.  
  459.     pval = apply_master_ob(QGET_PLURID,0);
  460.     if (pval && pval->type == T_POINTER)    
  461.     {
  462.     gPluid_list_d = pval->u.vec;
  463.     pval->u.vec->ref++;
  464.     }
  465.     else
  466.     gPluid_list_d = 0;
  467.  
  468.     pval = apply_master_ob(QGET_ADJID,0);
  469.     if (pval && pval->type == T_POINTER)
  470.     {
  471.     gAdjid_list_d = pval->u.vec;
  472.     pval->u.vec->ref++;
  473.     }
  474.     else
  475.     gAdjid_list_d = 0;
  476.  
  477.     pval = apply_master_ob(QGET_PREPOS,0);
  478.     if (pval && pval->type == T_POINTER)
  479.     {
  480.     gPrepos_list = pval->u.vec;
  481.     pval->u.vec->ref++;
  482.     }
  483.     else
  484.     gPrepos_list = 0;
  485.  
  486.     pval = apply_master_ob(QGET_ALLWORD,0);
  487.     if (pval && pval->type == T_STRING)
  488.     gAllword = string_copy(pval->u.string);
  489.     else
  490.     gAllword = 0;
  491.  
  492.     /* Loop through the pattern. Handle %s but not '/'
  493.     */
  494.     for (six=0,cix=0,fail=0,pix=0; pix < patvec->size; pix++)
  495.     {
  496.     pval = 0; 
  497.     fail = 0; 
  498.  
  499.     if (EQ(patvec->item[pix].u.string,"%s")) {
  500.         if (pix == (patvec->size-1))
  501.         {
  502.         pval = slice_words(wvec,cix,wvec->size-1);
  503.         cix = wvec->size;
  504.         }
  505.         else {
  506.         ocix = fword = cix; fpix = ++pix;
  507.         do {
  508.             fail = 0;
  509.             pval = sub_parse(obvec, patvec, &pix, wvec, &cix, &fail,
  510.                      (six<num_arg)?stack_args[six].u.lvalue:0);
  511.             if (fail) {
  512.             cix = ++ocix;
  513.             pix = fpix;
  514.             }
  515.         } while ((fail) && (cix<wvec->size));
  516.  
  517.         if (!fail) {
  518.             stack_put(pval,stack_args,six+1,num_arg);
  519.             pval = slice_words(wvec,fword,ocix-1);
  520.             stack_put(pval,stack_args,six++,num_arg);
  521.             pval = 0; 
  522.         }
  523.         }
  524.     }
  525.  
  526.     else if (!EQ(patvec->item[pix].u.string,"/")) {
  527.  
  528.         pval = sub_parse(obvec, patvec, &pix, wvec, &cix, &fail,
  529.                  (six<num_arg)?stack_args[six].u.lvalue:0);
  530.     }
  531.  
  532.     if ((!fail) && (pval))
  533.         stack_put(pval,stack_args,six++,num_arg);
  534.     else if (fail)
  535.         break;
  536.     }
  537.     
  538.     /* Also fail when there is words left to parse and pattern exhausted
  539.     */
  540.     if (cix != wvec->size)
  541.     fail = 1;
  542.  
  543.     /* Delete and free the id arrays
  544.     */
  545.     if (gId_list) 
  546.     {
  547.     gId_list->ref--;
  548.     free_vector(gId_list);
  549.     }
  550.     if (gPluid_list) 
  551.     {
  552.     gPluid_list->ref--;
  553.     free_vector(gPluid_list);
  554.     }
  555.     if (gAdjid_list) 
  556.     {
  557.     gAdjid_list->ref--;
  558.     free_vector(gAdjid_list);
  559.     }
  560.     if (gId_list_d) 
  561.     {
  562.     gId_list_d->ref--;
  563.     free_vector(gId_list_d); 
  564.     }
  565.     if (gPluid_list_d) 
  566.     {
  567.     gPluid_list_d->ref--;
  568.     free_vector(gPluid_list_d); 
  569.     }
  570.     if (gAdjid_list_d) 
  571.     {
  572.     gAdjid_list_d->ref--;
  573.     free_vector(gAdjid_list_d);
  574.     }
  575.     if (gPrepos_list) 
  576.     {
  577.     gPrepos_list->ref--;
  578.     free_vector(gPrepos_list);
  579.     }
  580.     if (gAllword)
  581.     free(gAllword);
  582.  
  583.     gId_list_d         = old_id_d;
  584.     gPluid_list_d    = old_plid_d;
  585.     gAdjid_list_d     = old_adjid_d;
  586.     gPrepos_list     = old_prepos;
  587.     gId_list         = old_id;     
  588.     gPluid_list     = old_plid; 
  589.     gAdjid_list     = old_adjid;
  590.     gAllword         = old_allword;
  591.  
  592.     wvec->ref--; 
  593.     patvec->ref--;
  594.     obvec->ref--;
  595.     free_vector(wvec);
  596.     free_vector(patvec);
  597.     if (ob_or_array->type == T_OBJECT) {
  598.     obvec->ref--;
  599.     free_vector(obvec);
  600.     }
  601.  
  602.     return !fail;
  603. }
  604.  
  605. /*
  606.  * Function name:     stack_put
  607.  * Description:        Puts an svalue on the stack.
  608.  * Arguments:        pval: Value to put
  609.  *            sp: Stackpointer
  610.  *            pos: Position on stack to put value
  611.  *            max: The number of args on the stack
  612.  */
  613. void stack_put(pval, sp, pos, max)
  614.     struct svalue    *pval;
  615.     struct svalue    *sp;
  616.     int            pos, max;
  617. {
  618.     if (pos >= max)
  619.     return;
  620.  
  621.     if ((pval) && (sp[pos].type == T_LVALUE))
  622.     assign_svalue(sp[pos].u.lvalue, pval);
  623. }
  624.  
  625. /*
  626.  * Function name:     slice_words
  627.  * Description:        Gives an imploded string of words from an array
  628.  * Arguments:        wvec: array of words
  629.  *            from: First word to use
  630.  *            to:   Last word to use
  631.  * Returns:        A pointer to a static svalue now containing string.
  632.  */
  633. struct svalue *slice_words(wvec, from, to)
  634.     struct vector    *wvec;
  635.     int            from, to;
  636. {
  637.     struct vector    *slice;
  638.     char        *tx;
  639.     static struct svalue stmp;
  640.  
  641.     if (from>to)
  642.     return 0;
  643.     
  644.     slice = slice_array(wvec, from, to);
  645.  
  646.     if (slice->size)
  647.     tx = implode_string(slice," ");
  648.     else
  649.     tx = 0;
  650.  
  651.     free_vector(slice);
  652.     if (tx) {
  653.     stmp.type = T_STRING;
  654.     stmp.string_type = STRING_SHARED;
  655.     stmp.u.string = make_shared_string(tx);
  656.     return &stmp;
  657.     }
  658.     else
  659.     return 0;
  660. }
  661.  
  662. /*
  663.  * Function name:     sub_parse
  664.  * Description:        Parses a vector of words against a pattern. Gives
  665.  *            result as an svalue. Sets fail if parsing fails and
  666.  *            updates pointers in pattern and word vectors. It
  667.  *            handles alternate patterns but not "%s"
  668.  */
  669. struct svalue *sub_parse(obvec, patvec, pix_in, wvec, cix_in, fail, sp)
  670.     struct vector    *obvec;
  671.     struct vector    *patvec;
  672.     int            *pix_in;
  673.     struct vector    *wvec;
  674.     int            *cix_in;
  675.     int            *fail;
  676.     struct svalue    *sp;        /* prepos_parse needs it */
  677. {
  678.     int        cix, pix, subfail;
  679.     struct svalue    *pval;
  680.     struct svalue    *one_parse();
  681.  
  682.     if (*cix_in == wvec->size) {
  683.     *fail = 1;
  684.     return 0;
  685.     }
  686.  
  687.     cix = *cix_in; pix = *pix_in; subfail = 0;
  688.  
  689.     pval = one_parse(obvec, patvec->item[pix].u.string,
  690.              wvec, &cix, &subfail, sp);
  691.  
  692.     while (subfail)
  693.     {
  694.     pix++;
  695.     cix = *cix_in;
  696.  
  697.     while ((pix < patvec->size) && (EQ(patvec->item[pix].u.string,"/")))
  698.     {
  699.         subfail = 0;
  700.         pix++;
  701.     }
  702.  
  703.     if ((!subfail) && (pix<patvec->size))
  704.         pval = one_parse(obvec, patvec->item[pix].u.string, wvec, &cix, 
  705.                  &subfail, sp);
  706.     else
  707.     {
  708.         *fail = 1; *pix_in = pix-1;
  709.         return 0;
  710.     }
  711.     }
  712.  
  713.     /* If there is alternatives skip them
  714.     */
  715.     if ((pix+1 < patvec->size) && (EQ(patvec->item[pix+1].u.string,"/"))) {
  716.     while ((pix+1 <patvec->size) &&
  717.            (EQ(patvec->item[pix+1].u.string,"/"))) {
  718.            pix += 2;
  719.     }
  720.     pix++; /* Skip last alternate after last '/' */
  721.     if (pix>=patvec->size)
  722.         pix = patvec->size-1;
  723.     }
  724.  
  725.     *cix_in = cix;
  726.     *pix_in = pix;
  727.     *fail = 0;
  728.     return pval;
  729. }
  730.     
  731.  
  732. /*
  733.  * Function name:     one_parse
  734.  * Description:        Checks one parse pattern to see if match. Consumes
  735.  *            needed number of words from wvec.
  736.  * Arguments:        obvec: Vector of objects relevant to parse
  737.  *            pat: The pattern to match against.
  738.  *            wvec: Vector of words in the command to parse
  739.  *            cix_in: Current word in commandword vector
  740.  *            fail: Fail flag if parse did not match
  741.  *            prep_param: Only used on %p (see prepos_parse)
  742.  * Returns:        svalue holding result of parse.
  743.  */
  744. struct svalue *one_parse(obvec, pat, wvec, cix_in, fail, prep_param)
  745.     struct vector    *obvec;
  746.     char        *pat;
  747.     struct vector    *wvec;
  748.     int            *cix_in;
  749.     int            *fail;
  750.     struct svalue    *prep_param;
  751. {
  752.     char ch;
  753.     struct svalue    *pval;
  754.     static struct svalue stmp;
  755.     char        *str1, *str2;
  756.     struct svalue    *item_parse();
  757.     struct svalue    *living_parse();
  758.     struct svalue    *single_parse();
  759.     struct svalue    *prepos_parse();
  760.     struct svalue    *number_parse();
  761.  
  762.     if (*cix_in == wvec->size) {
  763.     *fail = 1;
  764.     return 0;
  765.     }
  766.  
  767.     ch=pat[0]; 
  768.     if (ch=='%') {
  769.     ch=((isupper(pat[1]))?tolower(pat[1]):pat[1]);
  770.     }
  771.  
  772.     pval = 0;
  773.  
  774.     switch (ch) {
  775.     case 'i':
  776.     pval = item_parse(obvec, wvec, cix_in, fail);
  777.     break;
  778.  
  779.     case 'l':
  780.     pval = living_parse(obvec, wvec, cix_in, fail);
  781.     break;
  782.  
  783.     case 's':
  784.     *fail = 0; /* This is double %s in pattern, skip it */
  785.     break;
  786.  
  787.     case 'w':
  788.     stmp.type = T_STRING;
  789.     stmp.string_type = STRING_SHARED;
  790.     stmp.u.string = make_shared_string(wvec->item[*cix_in].u.string);
  791.     pval = &stmp;
  792.     (*cix_in)++;
  793.     *fail = 0;
  794.     break;
  795.  
  796.     case 'o':
  797.     pval = single_parse(obvec, wvec, cix_in, fail);
  798.     break;
  799.  
  800.     case 'p':
  801.     pval = prepos_parse(wvec, cix_in, fail, prep_param);
  802.     break;
  803.  
  804.     case 'd':
  805.     pval = number_parse(obvec, wvec, cix_in, fail);
  806.     break;
  807.  
  808.     case '\'':
  809.     str1 = &pat[1]; str2 = wvec->item[*cix_in].u.string;
  810.     if ((strncmp(str1,str2,strlen(str1)-1) == 0) &&
  811.         (strlen(str1) == strlen(str2)+1)) {
  812.         *fail = 0;
  813.         (*cix_in)++;
  814.     }
  815.     else
  816.         *fail = 1;
  817.     break;
  818.  
  819.     case '[':
  820.     str1 = &pat[1]; str2 = wvec->item[*cix_in].u.string;
  821.     if ((strncmp(str1,str2,strlen(str1)-1) == 0) &&
  822.         (strlen(str1) == strlen(str2)+1)) {
  823.         (*cix_in)++;
  824.     }
  825.     *fail = 0;
  826.     break;
  827.  
  828.     default:
  829.     *fail = 0; /* Skip invalid patterns */
  830.     }
  831.     return pval;
  832. }
  833.  
  834.  
  835. #ifndef PARSE_FOREIGN
  836.  
  837.     static char *ord1[] = {"", "first", "second", "third", "fourth", "fifth",
  838.                "sixth", "seventh", "eighth", "nineth", "tenth",
  839.                "eleventh", "twelfth", "thirteenth", "fourteenth",
  840.                "fifteenth", "sixteenth", "seventeenth", 
  841.                "eighteenth","nineteenth"};
  842.  
  843.     static char *ord10[] = {"", "", "twenty","thirty","forty","fifty","sixty",
  844.                 "seventy", "eighty","ninety"};
  845.     
  846.     static char *sord10[] = {"", "", "twentieth", "thirtieth", "fortieth",
  847.                  "fiftieth", "sixtieth","seventieth", "eightieth",
  848.                  "ninetieth"};
  849.  
  850.     static char *num1[] = {"", "one","two","three","four","five","six",
  851.                "seven","eight","nine","ten",
  852.                "eleven","twelve","thirteen","fourteen","fifteen",
  853.                "sixteen", "seventeen","eighteen","nineteen"};
  854.  
  855.     static char *num10[] = {"", "", "twenty","thirty","forty","fifty","sixty",
  856.                "seventy", "eighty","ninety"};
  857. #endif
  858.  
  859. /*
  860.  * Function name:     number_parse
  861.  * Description:        Tries to interpret the word in wvec as a numeral
  862.  *            descriptor and returns the result on the form:
  863.  *            ret.type == T_NUMBER
  864.  *                num == 0, 'zero', '0', gAllword
  865.  *                num > 0, one, two, three etc or numbers given
  866.  *                num < 0, first, second,third etc given
  867.  * Arguments:        obvec: Vector of objects relevant to parse
  868.  *            wvec: Vector of words in the command to parse
  869.  *            cix_in: Current word in commandword vector
  870.  *            fail: Fail flag if parse did not match
  871.  * Returns:        svalue holding result of parse.
  872.  */
  873. struct svalue *number_parse(obvec, wvec, cix_in, fail)
  874.     struct vector     *obvec;
  875.     struct vector     *wvec;
  876.     int            *cix_in;
  877.     int            *fail;
  878. {
  879.     int cix, ten, ones, num;
  880.     char buf[100];
  881.     static struct svalue stmp;
  882.  
  883.     cix = *cix_in; *fail = 0;
  884.  
  885.     if (sscanf(wvec->item[cix].u.string,"%d",&num)) {
  886.     if (num>=0) {
  887.         (*cix_in)++;
  888.         stmp.type = T_NUMBER;
  889.         stmp.u.number = num;
  890.         return &stmp;
  891.     }
  892.     *fail = 1;
  893.     return 0; /* Only nonnegative numbers */
  894.     }
  895.  
  896.     if (gAllword && (strcmp(wvec->item[cix].u.string,gAllword) == 0))
  897.     {
  898.     (*cix_in)++;
  899.     stmp.type = T_NUMBER;
  900.     stmp.u.number = 0;
  901.     return &stmp;
  902.     }
  903.     
  904.     for (ten=0;ten<10;ten++) for(ones=0;ones<10;ones++) {
  905.     sprintf(buf,"%s%s", num10[ten], (ten>1)?num1[ones]:num1[ten*10+ones]);
  906.     if (EQ(buf,wvec->item[cix].u.string)) {
  907.         (*cix_in)++;
  908.         stmp.type = T_NUMBER;
  909.         stmp.u.number = ten*10+ones;
  910.         return &stmp;
  911.     }
  912.     }
  913.  
  914.     for (ten=0;ten<10;ten++) for(ones=0;ones<10;ones++) {
  915.     sprintf(buf,"%s%s", (ones)?ord10[ten]:sord10[ten],
  916.         (ten>1)?ord1[ones]:ord1[ten*10+ones]);
  917.     if (EQ(buf,wvec->item[cix].u.string)) {
  918.         (*cix_in)++;
  919.         stmp.type = T_NUMBER;
  920.         stmp.u.number = -(ten*10+ones);
  921.         return &stmp;
  922.     }
  923.     }
  924.  
  925.     *fail = 1;
  926.     return 0;
  927. }
  928.  
  929.  
  930. /*
  931.  * Function name:     item_parse
  932.  * Description:        Tries to match as many objects in obvec as possible
  933.  *            onto the description given in commandvector wvec.
  934.  *            Also finds numeral description if one exist and returns
  935.  *            that as first element in array:
  936.  *            ret[0].type == T_NUMBER
  937.  *                num == 0, 'all' or 'general plural given'
  938.  *                num > 0, one, two, three etc given
  939.  *                num < 0, first, second,third etc given
  940.  *            ret[1-n] == Selected objectpointers from obvec
  941.  * Arguments:        obvec: Vector of objects relevant to parse
  942.  *            wvec: Vector of words in the command to parse
  943.  *            cix_in: Current word in commandword vector
  944.  *            fail: Fail flag if parse did not match
  945.  * Returns:        svalue holding result of parse.
  946.  */
  947. struct svalue *item_parse(obvec, wvec, cix_in, fail)
  948.     struct vector     *obvec;
  949.     struct vector     *wvec;
  950.     int            *cix_in;
  951.     int            *fail;
  952. {
  953.     struct vector    *tmp, *ret;
  954.     struct svalue    *pval;
  955.     static struct svalue stmp;
  956.     int            cix, tix, obix, plur_flag, max_cix, match_all;
  957.     int            match_object();
  958.     void        load_lpc_info();
  959.  
  960.  
  961.     tmp = allocate_array(obvec->size + 1);
  962.     if (pval = number_parse(obvec, wvec, cix_in, fail))
  963.     assign_svalue_no_free(&tmp->item[0],pval);
  964.  
  965.     if ((pval) && (pval->u.number>1))
  966.     {
  967.     plur_flag = 1;
  968.     match_all = 0;
  969.     }
  970.     else if ((pval) && (pval->u.number == 0))
  971.     {
  972.     plur_flag = 1;
  973.     match_all = 1;
  974.     }
  975.     else 
  976.     {
  977.     plur_flag = 0;
  978.     match_all = 0;
  979.     }
  980.  
  981.     for (max_cix = *cix_in, tix=1, obix=0; obix < obvec->size; obix++) {
  982.     *fail = 0; cix = *cix_in;
  983.     if (obvec->item[obix].type != T_OBJECT)
  984.         continue;
  985.     if (cix == wvec->size && match_all)
  986.     {
  987.         assign_svalue_no_free(&tmp->item[tix++],&obvec->item[obix]);
  988.         continue;
  989.     }        
  990.     load_lpc_info(obix,obvec->item[obix].u.ob);
  991.     if (match_object(obix, wvec, &cix, &plur_flag)) {
  992.         assign_svalue_no_free(&tmp->item[tix++],&obvec->item[obix]);
  993.         max_cix = (max_cix<cix)?cix:max_cix;
  994.     }
  995.     }
  996.  
  997.     if (tix<2) {
  998.     *fail = 1;
  999.     free_vector(tmp);
  1000.     if (pval) (*cix_in)--;
  1001.     return 0;
  1002.     }
  1003.     else
  1004.     {
  1005.     if (*cix_in < wvec->size)
  1006.         *cix_in = max_cix+1;
  1007.     ret = slice_array(tmp,0,tix-1);
  1008.     if (!pval) {
  1009.         ret->item[0].type = T_NUMBER;
  1010.         ret->item[0].u.number = plur_flag?0:1;
  1011.     }
  1012.     free_vector(tmp);
  1013.     }
  1014.  
  1015.     stmp.type = T_POINTER;
  1016.     stmp.u.vec = ret;
  1017.     return &stmp;
  1018. }
  1019.  
  1020. /*
  1021.  * Function name:     living_parse
  1022.  * Description:        Tries to match as many living objects in obvec as
  1023.  *            possible onto the description given in the command-
  1024.  *            vector wvec.
  1025.  *            Also finds numeral description if one exist and returns
  1026.  *            that as first element in array:
  1027.  *            ret[0].type == T_NUMBER
  1028.  *                num == 0, 'all' or 'general plural given'
  1029.  *                num > 0, one, two, three etc given
  1030.  *                num < 0, first, second,third etc given
  1031.  *            ret[1-n] == Selected objectpointers from obvec
  1032.  *            If not found in obvec a find_player and
  1033.  *            lastly a find_living is done. These will return an
  1034.  *            objecttype svalue.
  1035.  * Arguments:        obvec: Vector of objects relevant to parse
  1036.  *            wvec: Vector of words in the command to parse
  1037.  *            cix_in: Current word in commandword vector
  1038.  *            fail: Fail flag if parse did not match
  1039.  * Returns:        svalue holding result of parse.
  1040.  */
  1041. struct svalue *living_parse(obvec, wvec, cix_in, fail)
  1042.     struct vector     *obvec;
  1043.     struct vector     *wvec;
  1044.     int            *cix_in;
  1045.     int            *fail;
  1046. {
  1047.     struct vector    *live;
  1048.     struct svalue    *pval;
  1049.     static struct svalue stmp;
  1050.     struct object    *ob;
  1051.     int            obix, tix;
  1052.  
  1053.     live = allocate_array(obvec->size); tix = 0; *fail = 0;
  1054.  
  1055.     for (obix=0;obix<obvec->size;obix++) 
  1056.     if (obvec->item[obix].u.ob->flags & O_ENABLE_COMMANDS)
  1057.         assign_svalue_no_free(&live->item[tix++],&obvec->item[obix]);
  1058.  
  1059.     if (tix) {
  1060.     pval = item_parse(live, wvec, cix_in, fail);
  1061.     if (pval) {
  1062.         free_vector(live);
  1063.         return pval;
  1064.     }
  1065.     }
  1066.     
  1067.     free_vector(live);
  1068.  
  1069.     /* find_player */
  1070.     ob = find_living_object(wvec->item[*cix_in].u.string, 1); 
  1071.     if (!ob)
  1072.     /* find_living */
  1073.     ob = find_living_object(wvec->item[*cix_in].u.string, 0); 
  1074.  
  1075.     if (ob) {
  1076.     stmp.type = T_OBJECT;
  1077.     stmp.u.ob = ob;
  1078.     (*cix_in)++;
  1079.     return &stmp;
  1080.     }
  1081.     *fail = 1;
  1082.     return 0;
  1083. }
  1084.  
  1085. /*
  1086.  * Function name:     single_parse
  1087.  * Description:        Finds the first object in obvec fitting the description
  1088.  *            in commandvector wvec. Gives this as an objectpointer.
  1089.  * Arguments:        obvec: Vector of objects relevant to parse
  1090.  *            wvec: Vector of words in the command to parse
  1091.  *            cix_in: Current word in commandword vector
  1092.  *            fail: Fail flag if parse did not match
  1093.  * Returns:        svalue holding result of parse.
  1094.  */
  1095. struct svalue *single_parse(obvec, wvec, cix_in, fail)
  1096.     struct vector     *obvec;
  1097.     struct vector     *wvec;
  1098.     int            *cix_in;
  1099.     int            *fail;
  1100. {
  1101.     int            cix, obix, plur_flag;
  1102.     int            match_object();
  1103.     void            load_lpc_info();
  1104.  
  1105.     for (obix=0;obix<obvec->size;obix++) {
  1106.     *fail = 0; cix = *cix_in;
  1107.     load_lpc_info(obix,obvec->item[obix].u.ob);
  1108.     if (match_object(obix, wvec, &cix, &plur_flag)) {
  1109.         *cix_in = cix+1;
  1110.         return &obvec->item[obix];
  1111.     }
  1112.     }
  1113.     *fail = 1;
  1114.     return 0;
  1115. }
  1116.  
  1117. /*
  1118.  * Function name:     prepos_parse
  1119.  * Description:        This is a general sentencelist matcher with some hard-
  1120.  *            coded prepositions as the default list. The list is 
  1121.  *            sent as a parameter which will be replaced in the
  1122.  *            destination values. If no list is given the return
  1123.  *            value on match with the hardcoded prepositions will be
  1124.  *            string. If a list is given, the list will be returned
  1125.  *            with the matched sentence swapped to the first element.
  1126.  * Arguments:        wvec: Vector of words in the command to parse
  1127.  *            cix_in: Current word in commandword vector
  1128.  *            fail: Fail flag if parse did not match
  1129.  * Returns:        svalue holding result of parse.
  1130.  */
  1131. struct svalue *prepos_parse(wvec, cix_in, fail, prepos)
  1132.     struct vector     *wvec;
  1133.     int            *cix_in;
  1134.     int            *fail;
  1135.     struct svalue    *prepos;
  1136. {
  1137.   struct vector    *pvec, *tvec;
  1138.   static struct svalue stmp;
  1139.   char *tmp;
  1140.   int pix, tix;
  1141.  
  1142.   if ((!prepos) || (prepos->type != T_POINTER))
  1143.   {
  1144.       pvec = gPrepos_list;
  1145.   }
  1146.   else
  1147.   {
  1148.       pvec = prepos->u.vec;
  1149.   }
  1150.  
  1151.   for (pix = 0; pix < pvec->size; pix++)
  1152.   {
  1153.       if (pvec->item[pix].type != T_STRING)
  1154.       continue;
  1155.  
  1156.       tmp = pvec->item[pix].u.string;
  1157.       if (!strchr(tmp,' '))
  1158.       {
  1159.       if (EQ(tmp,wvec->item[*cix_in].u.string))
  1160.       {
  1161.           (*cix_in)++;
  1162.           break;
  1163.       }
  1164.       }
  1165.       else {
  1166.       tvec = explode_string(tmp, " ");
  1167.       for (tix=0;tix<tvec->size;tix++)
  1168.       {
  1169.           if ((*cix_in+tix >= wvec->size) ||
  1170.           (!EQ(wvec->item[*cix_in+tix].u.string,tvec->item[tix].u.string)))
  1171.           break;
  1172.       }
  1173.       if (tix = (tix == tvec->size)?1:0)
  1174.           (*cix_in)+=tvec->size;
  1175.       free_vector(tvec);
  1176.       if (tix)
  1177.           break;
  1178.       }
  1179.   }
  1180.  
  1181.   if (pix == pvec->size)
  1182.   {
  1183.       *fail = 1;
  1184.   }
  1185.   else if (pvec != gPrepos_list)
  1186.   {
  1187.       assign_svalue_no_free(&stmp,&pvec->item[0]);
  1188.       assign_svalue_no_free(&pvec->item[0],&pvec->item[pix]);
  1189.       assign_svalue_no_free(&pvec->item[pix], &stmp);
  1190.       *fail = 0;
  1191.   }
  1192.   return prepos;
  1193.  
  1194. }
  1195.  
  1196.  
  1197.  
  1198. /*
  1199.  * Function name:     match_object
  1200.  * Description:        Tests if a given object matches the description as
  1201.  *            given in the commandvector wvec.
  1202.  * Arguments:        obix: Index in id arrays for this object.
  1203.  *            wvec: Vector of words in the command to parse
  1204.  *            cix_in: Current word in commandword vector
  1205.  *            plur: This arg gets set if the noun was on pluralform
  1206.  * Returns:        True if object matches.
  1207.  */
  1208. int match_object(obix, wvec, cix_in, plur)
  1209.     int             obix;
  1210.     struct vector     *wvec;
  1211.     int            *cix_in;
  1212.     int            *plur;
  1213. {
  1214.     struct vector    *ids;
  1215.     int         il, pos, cplur, old_cix;
  1216.     char        *str;
  1217.     int            find_string();
  1218.     int            check_adjectiv();
  1219.  
  1220.     for (cplur = (*plur * 2); cplur<4; cplur++)
  1221.     {
  1222.     switch (cplur) 
  1223.     {
  1224.     case 0:
  1225.         if (!gId_list_d)
  1226.         continue;
  1227.         ids = gId_list_d;
  1228.         break;
  1229.  
  1230.     case 1:
  1231.         if (!gId_list || 
  1232.         gId_list->size <= obix || 
  1233.         gId_list->item[obix].type != T_POINTER)
  1234.         continue;
  1235.         ids = gId_list->item[obix].u.vec;
  1236.         break;
  1237.  
  1238.     case 2:
  1239.         if (!gPluid_list_d)
  1240.         continue;
  1241.         ids = gPluid_list_d;
  1242.         break;
  1243.  
  1244.     case 3:
  1245.         if (!gPluid_list || 
  1246.         gPluid_list->size <= obix || 
  1247.         gPluid_list->item[obix].type != T_POINTER)
  1248.         continue;
  1249.         ids = gPluid_list->item[obix].u.vec;
  1250.         break;
  1251.  
  1252.     default:
  1253.         ids = 0;
  1254.  
  1255.     }
  1256.  
  1257.     for (il = 0; il < ids->size; il++)
  1258.     {
  1259.         if (ids->item[il].type == T_STRING)
  1260.         {
  1261.         str = ids->item[il].u.string;  /* A given id of the object */
  1262.         old_cix = *cix_in;
  1263.         if ((pos = find_string(str, wvec, cix_in)) >= 0)
  1264.         {
  1265.             if (pos == old_cix)
  1266.             {
  1267.             if (cplur > 1)
  1268.                 *plur = 1;
  1269.             return 1;
  1270.             }
  1271.             else if (check_adjectiv(obix, wvec, old_cix, pos-1))
  1272.             {
  1273.             if (cplur > 1)
  1274.                 *plur = 1;
  1275.             return 1;
  1276.             }
  1277.         }
  1278.         *cix_in = old_cix;
  1279.         }
  1280.     }
  1281.     }
  1282.     return 0;
  1283. }
  1284.             
  1285.         
  1286. /*
  1287.  * Function name:     find_string
  1288.  * Description:        Finds out if a given string exist within an
  1289.  *            array of words.
  1290.  * Arguments:        str: String of some words 
  1291.  *            wvec: Array of words
  1292.  *            cix_in: Startpos in word array
  1293.  * Returns:        Pos in array if string found or -1
  1294.  */
  1295. int find_string(str, wvec, cix_in)
  1296.     char        *str;
  1297.     struct vector    *wvec;
  1298.     int            *cix_in;
  1299. {
  1300.     int fpos;
  1301.     char *p1, *p2;
  1302.     struct vector *split;
  1303.  
  1304.     for (; *cix_in < wvec->size; (*cix_in)++)
  1305.     {
  1306.     p1 = wvec->item[*cix_in].u.string;
  1307.     if (p1[0] != str[0])
  1308.         continue;
  1309.  
  1310.     if (strcmp(p1, str) == 0)
  1311.         return *cix_in;
  1312.  
  1313.     if (!(p2 = strchr(str,' ')))
  1314.         continue;
  1315.  
  1316.     /* If str was multi word we need to make som special checks
  1317.         */
  1318.     if (*cix_in == (wvec->size -1))
  1319.         continue;
  1320.  
  1321.     split = explode_string(str," ");
  1322.  
  1323.     /*
  1324.         wvec->size - *cix_in ==    2: One extra word
  1325.                 3: Two extra words
  1326.         */
  1327.     if (!split || (split->size >= (wvec->size - *cix_in))) {
  1328.         if (split) 
  1329.         free_vector(split);
  1330.         continue;
  1331.     }
  1332.     
  1333.     fpos = *cix_in;
  1334.     for (; (*cix_in-fpos) < split->size; (*cix_in)++)
  1335.     {
  1336.         if (strcmp(split->item[*cix_in-fpos].u.string, 
  1337.                wvec->item[*cix_in].u.string))
  1338.         break;
  1339.     }
  1340.     if ((*cix_in - fpos) == split->size)
  1341.         return fpos;
  1342.  
  1343.     *cix_in = fpos;
  1344.  
  1345.     }
  1346.     return -1;
  1347. }
  1348.  
  1349. /*
  1350.  * Function name:     check_adjectiv
  1351.  * Description:        Checks a word to see if it fits as adjectiv of an
  1352.  *            object.
  1353.  * Arguments:        obix: The index in the global id arrays
  1354.  *            wvec: The command words
  1355.  *            from: #1 cmdword to test
  1356.  *            to:   last cmdword to test
  1357.  * Returns:        True if a match is made.
  1358.  */
  1359. int check_adjectiv(obix, wvec, from, to)
  1360.     int            obix;
  1361.     struct vector    *wvec;
  1362.     int            from;
  1363.     int            to;
  1364. {
  1365.     int il, back, sum, fail;
  1366.     char *adstr;
  1367.     struct vector *ids;
  1368.     int member_string();
  1369.  
  1370.     if (gAdjid_list->item[obix].type == T_POINTER)
  1371.     ids = gAdjid_list->item[obix].u.vec;
  1372.     else
  1373.     ids = 0;
  1374.  
  1375.     for (sum = 0, fail = 0, il = from; il<= to; il++) 
  1376.     {
  1377.     sum += strlen(wvec->item[il].u.string) + 1;
  1378.     if ((member_string(wvec->item[il].u.string, ids) < 0) &&
  1379.         (member_string(wvec->item[il].u.string, gAdjid_list_d) < 0))
  1380.     {
  1381.         fail = 1;
  1382.     }
  1383.     }
  1384.  
  1385.     /* Simple case: all adjs were single word
  1386.     */
  1387.     if (!fail)
  1388.     return 1;    
  1389.  
  1390.     if (from == to)
  1391.     return 0;
  1392.  
  1393.     adstr = xalloc(sum); 
  1394.  
  1395.     for (il = from; il < to;) 
  1396.     {
  1397.     for (back = to; back > il; back--)
  1398.     {
  1399.         strcpy(adstr, "");
  1400.         for (sum = il; sum <= back; sum++)
  1401.         {
  1402.         if (sum > il)
  1403.             strcat(adstr, " ");
  1404.         strcat(adstr, wvec->item[sum].u.string);
  1405.         }
  1406.         if ((member_string(adstr, ids) >= 0) ||
  1407.         (member_string(adstr, gAdjid_list_d) >= 0))
  1408.         {
  1409.         il = back + 1;
  1410.         break;
  1411.         }
  1412.         free(adstr);
  1413.         return 0;
  1414.     }
  1415.     }
  1416.     free(adstr);
  1417.     return 1;
  1418. }
  1419.  
  1420.  
  1421. /*
  1422.  * Function name:     member_string
  1423.  * Description:        Checks if a string is a member of an array.
  1424.  * Arguments:        str: The string to search for
  1425.  *            svec: vector of strings
  1426.  * Returns:        Pos if found else -1.
  1427.  */
  1428. int member_string(str, svec)
  1429.     char        *str;
  1430.     struct vector    *svec;
  1431. {
  1432.     int il;
  1433.  
  1434.     if (!svec)
  1435.     return -1;
  1436.  
  1437.     for (il = 0; il < svec->size; il++)
  1438.     {
  1439.     if (svec->item[il].type != T_STRING)
  1440.         continue;
  1441.  
  1442.     if (strcmp(svec->item[il].u.string, str) == 0)
  1443.         return il;
  1444.     }
  1445.     return -1;
  1446. }
  1447.  
  1448. #ifndef PARSE_FOREIGN
  1449. /*
  1450.  * Function name:     parse_to_plural
  1451.  * Description:        Change a sentence in singular form to a sentence
  1452.  *            in pluralform.
  1453.  * Arguments:        str: The sentence to change
  1454.  * Returns:        Sentence in plural form.
  1455.  */
  1456. char *parse_to_plural(str)
  1457.     char *str;
  1458. {
  1459.     struct vector    *words;
  1460.     struct svalue    stmp;
  1461.     char *sp;
  1462.     int il, changed;
  1463.     char        *parse_one_plural();
  1464.  
  1465.     if (!(strchr(str,' ')))
  1466.     return parse_one_plural(str);
  1467.  
  1468.     words = explode_string(str, " ");
  1469.     
  1470.     for (changed = 0, il = 1; il < words->size; il++) {
  1471.     if ((EQ(words->item[il].u.string,"of")) ||
  1472.         (il+1 == words->size))  {
  1473.         sp = parse_one_plural(words->item[il-1].u.string);
  1474.         if (sp != words->item[il-1].u.string) {
  1475.         stmp.type = T_STRING;
  1476.         stmp.string_type = STRING_MALLOC;
  1477.         stmp.u.string = string_copy(sp);
  1478.         assign_svalue(&words->item[il-1],&stmp);
  1479.         changed = 1;
  1480.         }
  1481.     } 
  1482.     }
  1483.     if (!changed) {
  1484.     free_vector(words);
  1485.     return str;
  1486.     }
  1487.     sp = implode_string(words, " ");
  1488.     free_vector(words);
  1489.     return sp;
  1490. }
  1491.     
  1492.  
  1493. /*
  1494.  * Function name:     parse_one_plural
  1495.  * Description:        Change a noun in singularform to a noun
  1496.  *            in pluralform.
  1497.  * Arguments:        str: The sentence to change
  1498.  * Returns:        Word in plural form.
  1499.  */
  1500. char *parse_one_plural(str)
  1501.     char    *str;
  1502. {
  1503.     char     ch, ch2;
  1504.     int     sl;
  1505.     static char    pbuf[100];   /* Only stupid people finds words > 100 letters */
  1506.     
  1507.     sl=strlen(str) - 1; 
  1508.     if ((sl<2) || (sl>90))
  1509.     return str;
  1510.  
  1511.     ch = str[sl];
  1512.     ch2 = str[sl-1];
  1513.     strcpy(pbuf, str); pbuf[sl] = 0;
  1514.  
  1515.     switch (ch)
  1516.     {
  1517.     case 's':
  1518.     case 'x':
  1519.     case 'h':
  1520.     return strcat(pbuf, "ses");
  1521.     case 'y':
  1522.     return strcat(pbuf, "ies");
  1523.     case 'e':
  1524.     if (ch2 == 'f')
  1525.     {
  1526.         pbuf[sl-1] = 0;
  1527.         return strcat(pbuf, "ves");
  1528.     }
  1529.     }
  1530.     
  1531.     if (EQ(str,"corpse")) return "corpses";
  1532.     if (EQ(str,"tooth")) return "tooth";
  1533.     if (EQ(str,"foot")) return "foot";
  1534.     if (EQ(str,"man")) return "men";
  1535.     if (EQ(str,"woman")) return "women";
  1536.     if (EQ(str,"child")) return "children";
  1537.     if (EQ(str,"sheep")) return "sheep";
  1538.  
  1539.     pbuf[sl] = ch;
  1540.     return strcat(pbuf, "s");
  1541. }
  1542. #endif
  1543.  
  1544. /*
  1545.  
  1546.    End of Parser
  1547.  
  1548. ***************************************************************/
  1549.  
  1550.  
  1551. /*
  1552.      ----- describe: This code is obsolete ------
  1553. */
  1554.  
  1555. char *describe_items (arr, func, live)
  1556.     struct svalue *arr;
  1557.     char *func;
  1558.     int live;
  1559. {
  1560.     return "Not supported in 3.0";
  1561. }
  1562.  
  1563. /* process_string
  1564.  *
  1565.  * Description:   Checks a string for the below occurences and replaces:
  1566.  *          Fixes a call to a named function if the value is on the
  1567.  *                form: '@@function[:filename][|arg|arg]@@' Filename is
  1568.  *                optional.
  1569.  *          Note that process_string does not recurse over returned
  1570.  *          replacement values. If a function returns another function
  1571.  *          description, that description will not be replaced.
  1572.  * Arguments:     str: A string containing text and function descriptions as
  1573.  *          as described above.
  1574.  * Returns:       String containing the result of all replacements.
  1575.  */
  1576. char *
  1577. process_string(str)
  1578.     char *str;
  1579. {
  1580.     struct vector *vec;
  1581.     struct svalue *ret;
  1582.     struct object *old_cur = current_object;
  1583.     int pr_start, il, changed, ch_last;
  1584.     char *p1, *p2, *p3, *buf, *old_eff_user;
  1585.     char *process_part();
  1586.  
  1587.     if ((!str) || (!(p1=strchr(str,'@'))))
  1588.     return str;
  1589.  
  1590.     /* This means we are called from notify_ in comm1 
  1591.        We must temporary set eff_user to backbone uid for
  1592.        security reasons.
  1593.     */
  1594.  
  1595.     old_eff_user = 0;
  1596.  
  1597.     if (!current_object)
  1598.     {
  1599.     current_object = command_giver;
  1600.     ret = apply_master_ob("get_bb_uid",0);
  1601.     if (!ret || ret->type != T_STRING)
  1602.         return str;
  1603.     if (current_object->eff_user)
  1604.     {
  1605.         old_eff_user = current_object->eff_user->name;
  1606.         current_object->eff_user = add_name(ret->u.string);
  1607.     }
  1608.     }
  1609.     
  1610.     vec = explode_string(str,"@@");
  1611.     if (!vec)
  1612.     return str;
  1613.  
  1614.     pr_start = ((str[0]=='@') && (str[1]=='@'))?0:1;
  1615.  
  1616.     for (ch_last = 0, changed = 0, il = pr_start; il < vec->size; il++) {
  1617.     p1 = strchr(vec->item[il].u.string,' ');
  1618.     if (!p1) {
  1619.         p2 = process_part(vec->item[il].u.string);
  1620.         if (p2 != vec->item[il].u.string) 
  1621.         ch_last = 1;
  1622.     }
  1623.     else
  1624.     {
  1625.         buf = string_copy(vec->item[il].u.string);
  1626.         p1 = strchr(buf,' ');
  1627.         *p1=0;
  1628.         p2 = process_part(buf);
  1629.         *p1=' ';
  1630.         if (p2 != buf)
  1631.         {
  1632.         p3 = xalloc(1+strlen(p1)+strlen(p2));
  1633.         strcpy(p3,p2);
  1634.         strcat(p3,p1);
  1635.         free(p2);
  1636.         p2 = p3;
  1637.         }
  1638.         else
  1639.         p2 = vec->item[il].u.string;
  1640.  
  1641.         free(buf);
  1642.     }
  1643.     if (p2 == vec->item[il].u.string) {
  1644.         if(!ch_last) {              /* get rid of the last snabels */
  1645.         p3 = xalloc(3+strlen(p2));
  1646.         strcpy(p3,"@@"); strcat(p3,p2);
  1647.         p2 = p3;
  1648.         }
  1649.         else
  1650.         ch_last = 0;
  1651.     }
  1652.     else
  1653.         changed = 1;
  1654.  
  1655.     if (p2 != vec->item[il].u.string)
  1656.     {
  1657.         free_svalue(&vec->item[il], "process_string");
  1658.         vec->item[il].u.string = string_copy(p2);
  1659.         vec->item[il].string_type = STRING_MALLOC;
  1660.         vec->item[il].type = T_STRING;
  1661.         free(p2);
  1662.     }
  1663.     }
  1664.  
  1665.     if (changed)
  1666.     buf = implode_string(vec, "");
  1667.     else
  1668.     buf = 0;
  1669.  
  1670.     free_vector(vec);
  1671.  
  1672.     if (old_eff_user)
  1673.     {
  1674.     current_object->eff_user = add_name(old_eff_user);
  1675.     }
  1676.  
  1677.     current_object = old_cur;
  1678.  
  1679.     return (buf)?buf:str;
  1680. }
  1681.  
  1682. /*
  1683.  * Process a string holding exactly one 'value by function call'
  1684.  */
  1685. char *process_part(str)
  1686.     char    *str;
  1687. {
  1688.     struct svalue *ret;
  1689.     char *func,*obj,*arg,*narg;
  1690.     int numargs;
  1691.     struct object *ob;
  1692.  
  1693.     if ((strlen(str)<1) || (str[0]<'A') || (str[0]>'z'))
  1694.     return str;
  1695.  
  1696.     func = string_copy(str);
  1697.  
  1698.     arg = strchr(func,'|'); if (arg) { *arg=0; arg++; }
  1699.     obj = strchr(func,':'); if (obj) { *obj=0; obj++; }
  1700.  
  1701.     /* Find the objectpointer
  1702.     */
  1703.     if (!obj) 
  1704.     ob = current_object;
  1705.     else
  1706.     ob = find_object2(obj);
  1707.  
  1708.     if (!ob)
  1709.     {
  1710.     free(func);
  1711.     return str;
  1712.     }
  1713.  
  1714.     /* Push all arguments as strings to the stack
  1715.     */
  1716.     for (numargs = 0; arg; arg=narg)
  1717.     {
  1718.     narg = strchr(arg,'|');
  1719.     if (narg) 
  1720.         *narg = 0; 
  1721.     push_string(arg,STRING_MALLOC); numargs++;
  1722.     if (narg) 
  1723.     {
  1724.         *narg = '|'; narg++;
  1725.     }
  1726.     }
  1727.     
  1728.     /* Apply the function and see if adequate answer is returned
  1729.     */
  1730.     ret = apply(func, ob, numargs);
  1731.  
  1732.     free(func);
  1733.  
  1734.     if ((ret) && (ret->type == T_STRING))
  1735.     return string_copy(ret->u.string);
  1736.     else
  1737.     return str;
  1738. }
  1739.