musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/*
 * MusicMUD Daemon, version 1.0
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "musicmud.h"
#include "MudObject.h"
#include "verbs.h"
#include "match.h"
#include "util.h"
#include "misc.h"
#include "levels.h"
#include "units.h"
#include "colour.h"
#include "pflags.h"

#include <string>
#include <vector>
#include <algorithm>
#include <ctype.h>

void sett(MudObject *o, string prop, TeleObject what)
{
  o->set(prop.c_str(),       what->id);

  if (what->owner != what.where) {
    o->set((prop+"n").c_str(), what.nth);
    o->set((prop+"o").c_str(), what.where->id);
  } else {
    o->unset((prop+"n").c_str());
    o->unset((prop+"o").c_str());
  }
}
void set_prons(MudObject *player, TeleObject what)
{
  if (what.what == player)
    return;

  gender_t g = get_gender(what);
  if (g==GENDER_MALE)
    sett(player, "!him", what);
  else if (g==GENDER_FEMALE)
    sett(player, "!her", what);
  else if (g==GENDER_PLURAL || g==GENDER_ANDRO)
    sett(player, "!them", what);
  else
    sett(player, "!it", what);
}

void set_prons(MudObject *player, const MudObjectWorld &things)
{
  if (things.getsize()==1) {
    set_prons(player, things.get(0));
    return;
  }

  if (things.getsize()==0) {
    return;
  }

  player->unset("!them");
  player->unset("!themo");
  player->unset("!themp");

  MudObject *o;
  int i;

  foreach((&things), o, i) {
    player->array_set("!them", i, o);
    player->array_unset("!them", i, "o");
    player->array_unset("!them", i, "n");
  }
  player->set("!them.count", i);
}

bool endswith(const char *a, const char *b) {
  if (strlen(b)>strlen(a)) return 0;
  a += strlen(a) - strlen(b);
  return !strcmp(a, b);
}

const char *ord[] = {
  "first",
  "second",
  "third",
  "fourth",
  "fifth",
  "sixth",
  "seventh",
  "eighth",
  "ninth",
  "tenth",
  "eleventh",
  "twelfth",
  "thirteenth",
  "fourteenth",
  "fifteenth",
};

int height(MudObject *o) {
  MudObject *upon = o->get_object(KEY_SITON);
  if (upon)
    return upon->get_int("height", 1000);
  return 0;
}

int eyelevel(MudObject *o) {
  return o->get_int("eyelevel", 1500) + height(o) +
    o->get_flag(FL_SITTING)*-500 +
    o->get_flag(FL_SLEEPING)*-1000;
}

bool low_enough(MudObject *who, MudObject *what)
{
  return height(what) <= eyelevel(who);
}

bool shelf_low_enough(MudObject *who, MudObject *what)
{
  return what->get_int("height", 0) <= eyelevel(who);
}

static int deordinator(const char *a) {
  if (!a)
    return -1;

  for(int i=0;i<15;i++) {
    if(streq(ord[i], a))
      return i;
  }

  char *e = 0;
  long what = strtol(a, &e, 10);
  if (*e && (e-a+2)!=(int)strlen(a)) {
    return -1;
  }

  if (what<1) {
    return -1;
  }

  if (endswith(a, "1st")) {
    return what-1;
  }
  if (endswith(a, "2nd")) {
    return what-1;
  }
  if (endswith(a, "3rd")) {
    return what-1;
  }

  if (endswith(a, "th")) {
    return what-1;
  }

  return -1;
}


struct {int n;const char *str;} card[] = {
  { 0, "no", },
  { 1, "a",},
  { 1, "an",},
  { 1, "one",},
  { 2, "two",},
  { 2, "couple",},
  { 3, "three",},
  { 4, "four",},
  { 5, "five",},
  { 6, "six",},
  { 7, "seven",},
  { 8, "eight",},
  { 9, "nine",},
  { 10, "ten",},
  { 11, "eleven",},
  { 12, "twelve",},
  { 12, "dozen",},
  { 13, "thirteen",},
  { 14, "fourteen",},
  { 15, "fifteen", },
  {-1, NULL },
};

int decardinator(const char *a) {
  if (!a || !*a)
    return -1;

  char *e = NULL;
  long l = strtol(a, &e, 10);
  if (!*e && l >= 0)
    return l;

  for(int i=0;card[i].str;i++) {
    if(streq(card[i].str, a))
      return card[i].n;
  }

  return -1;
}

static bool namesmatch2(const char *o_name, const char *sought) {
  while (o_name) {
    while (*o_name==' ') o_name++;
    string oname = lose_colour(o_name);
    if (oname.length() >= 3 && strlen(sought)<3) {
      o_name = strchr(o_name, ' ');
      continue;
    }
    if (strncasecmp(oname.c_str(), sought, strlen(sought))==0) return true;
    o_name = strchr(o_name, ' ');
  }
  return false;
}

static bool isshort(const char *ch)
{
  if (streq(ch, "a")) return 1;
  if (streq(ch, "an")) return 1;
  if (streq(ch, "of")) return 1;
  if (streq(ch, "the")) return 1;
  if (streq(ch, "some")) return 1;
  return 0;
}

bool names_match(const char *haystack, const char *needle)
{
  if (!haystack)
    return 0;
  if (!needle)
    return 0;

  char *neadle = strdup(needle);

  char *n2 = neadle + strlen(neadle);
  n2--;
  if (*n2=='s' && strlen(neadle)>1) {
    *n2 = 0;
    if (isshort(neadle)) {
      free(neadle);
      return 0;
    }
  }

  char *a = strdup(haystack), *b=a;
  a = strtok(a, " ");
  while (a) {
    if (namesmatch2(a, neadle)) {
      free(neadle);
      free(b);
      return 1;
    }
    a = strtok(NULL, " ");
  }
  free(neadle);
  free(b);
  return 0;
}


static bool jewel(MudObject *what) 
{
  const char *w = what->get("wornon");
  if (!w) return 0;
  if (streq(w, "finger")) return 1;
  if (streq(w, "wrist")) return 1;
  if (streq(w, "neck")) return 1;
  if (streq(w, "lapel")) return 1;
  int wl = what->get_int("wornlevel", 0);
  if (wl==4) return 1;
  return 0;
}

struct MatchInfo
{
  int rating;
  TeleObject ob;
  bool operator<(const MatchInfo&mi) const {
    return rating > mi.rating;
  }
  MatchInfo(int r, TeleObject o) : rating(r), ob(o) {
  }
};

typedef vector<MatchInfo> Matches;

bool category_match(MudObject *what, const char *str, MudObject *me) {

  if (streq(str, "player") && is_player(what) && me != what)
    return 1;

  if (streq(str, "mobile") && is_mobile(what) && me != what)
    return 1;

  if (streq(str, "wielded") && what->owner && what->owner->get_object(KEY_WIELD)==what) 
    return 1;

  if (streq(str, "worn") && what->owner == what->get_object(KEY_WORNBY)) 
    return 1;

  if (streq(str, "unworn") && what->owner != what->get_object(KEY_WORNBY) && what->owner==me) 
    return 1;

  if (streq(str, "held")) {
    if (what->owner != me)
      return 0;

    if (!what->owner)
      return 0;

    if (what->owner == what->get_object(KEY_WORNBY))
      return 0;

    if (what->owner->get_object(KEY_WIELD)==what)
      return 0;

    if (mount(what->owner)==what)
      return 0;

    return 1;
  }

  if (what->get_flag(FL_MOBILE))
    return 0;

  if (is_player(what))
    return 0;

  if (what->get_int("food")!=-1 || what->get_int("alcohol")!=-1) {
    if (!what->get_flag(FL_DRINK) && streq(str, "food"))
      return 1;
    if (what->get_flag(FL_DRINK) && streq(str, "drink"))
      return 1;
  }
  if (what->get_int("damage")>=1 && streq(str, "weapon"))
    return 1;
  if (what->get_int("heal")>=1 && streq(str, "medical"))
    return 1;
  if (what->get("wornon") && what->get_int("heal")<1) {
    if (jewel(what) && (streq(str, "jewelry") ||
			(streq(str, "jewellery"))))
      return 1;
    
    if (!jewel(what) && streq(str, "clothing"))
      return 1;
  }
  if (what->get_flag(FL_MOUNT) && streq(str, "vehicle"))
    return 1;
  if (what->get_flag(FL_WATERTIGHT) && streq(str, "empty") && used_volume(what)==0)
    return 1;
  return 0;
}

void TeleWorld::add(TeleObject ob) {
  v.push_back(ob);
  siz++;
}

bool TeleWorld::has(const TeleObject &ob) const {
  for (int i = 0;i<getsize();i++)
    if (get(i)==ob)
      return 1;
  return 0;
}

void TeleWorld::add(MudObject &what, MudObject *where) {
  TeleObject a(where, &what, 0);
  v.push_back(a);
  siz++;
}


void TeleWorld::add(MudObject &what, MudObject *where, int n) {
  TeleObject a(where, &what, n);
  v.push_back(a);
  siz++;
}

int TeleWorld::getsize() const {
  return v.size();
}

const TeleObject &TeleWorld::get(int a) const {
  return v[a];
}

MudObject *TeleWorld::get_nth(int a) const {
  return v[a].what;
}

int TeleWorld::get_nthnth(int a) const {
  return v[a].nth;
}

static void addroom(TeleWorld &tomatch, MudObject *where, int flags, MudObject *forwho, int needglow) {
  MudObject *o;
  int i;
  MudObject *fl=floor(where);
  if (fl)
    tomatch.add(*fl, where);
  
  foreach(where->children, o, i)
    if (o != fl)
      if (((!o->get_flag(FL_EXIT) || !(flags & IGNORE_EXITS))))
	if (!needglow || o->get_flag(FL_GLOWING))
	  if (low_enough(forwho, o)) {
	    if (!(flags & PERSON_ONLY) || is_person(o))
	      tomatch.add(*o, where);
	  }
  
  if (flags & LOOK_MWIELD) {
    foreach(where->children, o, i) 
      if (o != forwho) {
	if (MudObject *wpn = weapon(o))
	  if (((!wpn->get_flag(FL_EXIT) || !(flags & IGNORE_EXITS)))) {
	    if (!needglow || wpn->get_flag(FL_GLOWING))
	      if (!(flags & PERSON_ONLY) || is_person(o))
		tomatch.add(*wpn, o);
	  }
	
	if (MudObject *m = mount(o))
	  if (m->owner==o)
	    if (((!m->get_flag(FL_EXIT) || !(flags & IGNORE_EXITS)))) {
	      if (!needglow || m->get_flag(FL_GLOWING))
		if (!(flags & PERSON_ONLY) || is_person(o))
		  tomatch.add(*m, o);
	    }
      }
  }
  
  for (int i=0;i<where->array_size("tele");i++) {
    o = where->array_get_object("tele", i);
    if (fl == o)
      continue;
    if (!(flags & PERSON_ONLY) || is_person(o))
    if (!needglow || o->get_flag(FL_GLOWING)) {
      int c = where->array_get_int("tele", i, "count", 1);
      if (c > 10) c = 10;
      for (int j=0;j<c;j++) {
	tomatch.add(*o, where, j);
      }
    }
  }
  
  if (forwho->get_int(KEY_BUNNIED, 0)>1) {
    MudObject *bun = MUD_BUNNIES;
    if (!(flags & PERSON_ONLY) || is_person(bun))
    if (bun)
      if (!needglow || bun->get_flag(FL_GLOWING))
	tomatch.add(*bun, where);
  }
}

TeleObject gett(MudObject *o, string prop)
{
  MudObject *obj = o->get_object(prop.c_str());
  if (!obj)
    return TeleObject();

  TeleObject to(o->get_object((prop+"o").c_str())?:obj->owner, 
	       obj, 
	       o->get_int((prop+"n").c_str(), 0));
  return to;
}

TeleObject gett(MudObject *o, string prop, int i)
{
  MudObject *obj = o->array_get_object(prop.c_str(), i);
  if (!obj) {
    return TeleObject();
  }
  
  TeleObject to(o->array_get_object(prop.c_str(), i, "o")?:obj->owner,
		obj, 
		o->array_get_int(prop.c_str(), i, "n", 0));
  return to;
}

static Matches match(MudObject *forwho, int argc, const char **argv, tfn_t fn,
	       int flags, int &all_nothing, World<MudObject> *special)
{
  MudObject *mission = forwho->get_object(KEY_ACCEPTED);
  
  Matches mi;

  /* TODO : BUNNIES
     consider virtual objects putting back? */
  
  TeleWorld tomatch;

  MudObject *o;
  int i;

  MudObject *where = forwho->owner;

  if (flags & LOOK_ROOM) 
    /* automatically let 'me' be used if room is allowed */
    flags |= ALLOW_ME;

  int roomneedglow = 0;

  if (!cansee(forwho) || !where || ((where->get_flag(FL_ONFIRE)||!has_light(where))&&!wf_wears_with_flag(forwho, FL_NIGHTVISION))) {
    //    flags &= ~LOOK_ROOM;
    roomneedglow = 1;
    /* if the room is dark, then any objects that aren't glowing cannot be seen or even referred to */
  }

  if (flags & LOOK_INV) 
    foreach(forwho->children, o, i)
      if (((!o->get_flag(FL_EXIT) || !(flags & IGNORE_EXITS)))
	  && (o!=forwho))
	tomatch.add(*o, forwho);
  /* add your inventory to the matcher if you're looking there */

  if (flags & LOOK_LINKED) {
    foreach(where->children, o, i) {
      MudObject *e = linkedlink(o);
      if (!e)
	continue;

      addroom(tomatch, e, flags|PERSON_ONLY, forwho, roomneedglow);
    }
  }

  if (flags & LOOK_ROOM && where) {
    addroom(tomatch, where, flags, forwho, roomneedglow);
  }
  /* add contents of linked and rooms you're in */

  if (flags & LOOK_SPECIAL && special)
    foreach(special, o, i) 
      if (((!o->get_flag(FL_EXIT) || !(flags & IGNORE_EXITS))))
	tomatch.add(*o, where);
  /* add contents of Special paramter if specified */

  if (argc) {
    all_nothing = 0;
  }

  if (argc==1 && streq(argv[0], "$1")) {
    /* deal with $1 magically */
    MudObject *o1 = forwho->get_object("!$1");
    if (o1) {
      int yes = 0;

      if (flags & LOOK_ROOM && o1->owner == forwho->owner && (!roomneedglow || o1->get_flag(FL_GLOWING)))
	yes = 1;

      if (flags & LOOK_INV && o1->owner == forwho)
	yes = 1;

      if (yes) {
	MatchInfo j(1100, o1);
	mi.push_back(j);
      }
    }
  }

  if (argc==1) {
    if (flags & ALLOW_ME) {
      /* Me matches first */
      if (streq(argv[0], "myself") || streq(argv[0], "me")) {
	MatchInfo j(1100, forwho);
	mi.push_back(j);
      }
    }
  
    if (streq(argv[0], "it")) {
      if (TeleObject it=gett(forwho, "!it")) {
	if (tomatch.has(it)) {
	  MatchInfo j(1100, it);
	  mi.push_back(j);
	  return mi;
	}
      }
    }

    if (streq(argv[0], "him")) {
      if (TeleObject it=gett(forwho, "!him")) {
	if (tomatch.has(it)) {
	  MatchInfo j(1100, it);
	  mi.push_back(j);
	  return mi;
	}
      }
    }

    if (streq(argv[0], "her")) {
      if (TeleObject it=gett(forwho, "!her")) {
	if (tomatch.has(it)) {
	  MatchInfo j(1100, it);
	  mi.push_back(j);
	  return mi;
	}
      }
    }

    if (streq(argv[0], "them")) {
      if (TeleObject it=gett(forwho, "!them")) {
	if (tomatch.has(it)) {
	  MatchInfo j(1100, it);
	  mi.push_back(j);
	  return mi;
	}
      }
      if (int c = forwho->array_size("!them")) {
	for (int i=0;i<c;i++) {
	  TeleObject it = gett(forwho, "!them", i);
	  //	  if (it) {
	  //	    //	    ::printf("Matched %s\n", name(it));
	  //	  }
	  if (it && tomatch.has(it)) {
	    MatchInfo j(1100, it);
	    mi.push_back(j);
	  }
	}
	return mi;
      }
    }
  }

  foreach((&tomatch), o, i)
   if (o && visible_to(forwho, o)) {

     TeleObject ob = tomatch.get(i);

    const char *oshort = o->get("short");
    const char *rname = name(o);
    const char *pname = 0;
    string ownames;
    string pnames;

    if (ob.where && is_person(ob.where) && ob.where != forwho) {
      ownames = ob.where->get("short");
      ownames += "'s";
    }
    const char *owname = ownames.length()?ownames.c_str():NULL;

    if (rname) {
      pnames = plural_name(o);
      pname = pnames.c_str();
    }

    const char *lname = o->get("long");

    const char *altshort = o->get("altshort");

    if (argc==1) {
      if ((is_mobile(forwho) || forwho->get_priv(PFL_SEESTATS)) && 
	  streq(o->id, argv[0])) {
	/* match on id first, but only for captains (and mobs */
	MatchInfo j((!fn||fn(ob))?1050:550, ob);
	mi.push_back(j);
	continue;
      }

      if (streq(oshort, argv[0])) {
	/* match by short */
	MatchInfo j((!fn||fn(ob))?1000:500, ob);
	mi.push_back(j);
	continue;
      }
      if (streq(altshort, argv[0])) {
	/* match by altshort */
	MatchInfo j((!fn||fn(ob))?950:450, ob);
	mi.push_back(j);
	continue;
      }
    }
    
    if (argc) {
      /* then match by a bunch of other things. words must exist in one of these
         and then they're ok, if any non-ok words are given, its not ok */
      int clash = 0, ok = 0;
      for (int j=0;j<argc;j++) {
	if (isshort(argv[j])) {
	  if (names_match(oshort, argv[j]) ||
	      names_match(altshort, argv[j])) {
	    ok = 1;
	  }
	}
	
	if (!names_match(oshort,    argv[j])
	    && !names_match(rname,     argv[j])
	    && !names_match(pname,     argv[j])
	    && !names_match(lname,     argv[j])
	    && !names_match(altshort, argv[j])	    
	    && !names_match(owname, argv[j]) 
	    && !category_match(o, argv[j], forwho)) {
	  clash = 1;
	} else {
	  if (!isshort(argv[j])) {
	    ok = 1;
	  }
	}
      }
      if (!clash && ok) {
	MatchInfo j((!fn||fn(ob))?900:400, ob);
	mi.push_back(j);
	continue;
      }
    }

    if (!fn || fn(ob)) {
      /* deal with 'all'. which is handled by giving this argc 0 */
      if (argc==0 && o != forwho) {
	if (flags & IGNORE_MISSION && (o == mission))
	  continue;
	
	if (o->get_flag(FL_INVISIBLE))
	  continue;

	MatchInfo j((!fn||fn(ob))?800:300, ob);
	mi.push_back(j);
	continue;
      }
      
      if (argc==1 && !(flags&IGNORE_CATEGORIES)) {
	if (category_match(o, argv[0], forwho))
	  {
	    MatchInfo j((!fn||fn(o))?600:200, ob);
	    mi.push_back(j);
	  }
      }
    }
  }

  /* sort in order of goodness */
  sort(mi.begin(), mi.end());
  
  return mi;
}

static Matches match2(MudObject *forwho, int argc, const char **argv, tfn_t fn,
	      int flags, int &all_nothing, World<MudObject> *special) {
  
  int all = 0;
  int nth = 0;
  int count = -1;
  int any = 0;

  if (flags & DEFAULT_ALL)
    all = 1;

  if (argc && streq(argv[0], "all")) {
    argc--;
    argv++;
    all = 1;
    /* all (except missions) */
  }

  if (argc && streq(argv[0], "ALL")) {
    argc--;
    argv++;
    all = 1;
    /* all (including missions */
    flags &= ~IGNORE_MISSION;
  }

  if (argc && streq(argv[0], "any")) {
    argc--;
    argv++;
    if (!argc) {
      Matches m;
      all_nothing = 103;
      return m;
    }
    all = 0;
    any = 1;
    /* return a random one of the candidates */
  }

  const char *replaced_with = 0;
  int replaced = 0;

  if (argc && strncmp(argv[0], "far.", 4)==0) {
    replaced = 0;
    replaced_with = argv[0];
    argv[0] += 4;
    flags &= ~LOOK_BOTH;
  }

  /* This means that callers must always check the result to see if its valid. */

  if (argc && strncmp(argv[0], "near.", 5)==0) {
    replaced = 0;
    replaced_with = argv[0];
    argv[0] += 5;
    flags &= ~LOOK_SPECIAL;
  }

  if (argc && streq(argv[0], "that") || streq(argv[0], "those")) {
    argc--;
    argv++;
    flags &= ~LOOK_INV;
    /* that and those look only in the room */
  }

  if (argc && streq(argv[0], "these") || streq(argv[0], "my")) {
    argc--;
    argv++;
    flags &= ~LOOK_ROOM;
    /* these and my look only in your inv */
  }

  if (argc) {
    /* convert 1st, 2nd, 3rd, etc to an ordinal number */
    nth = deordinator(argv[0]);
    if (nth == -1)
      nth = 0;
    else {
      argc--;
      argv++;
      }

    /* convert cardinal numbers */
    if (argc>1) {      
      count = decardinator(argv[0]);
      if (count != -1) {
	argc--;
	argv++;
	all = 0;
      }
    }
  }

  /* deal with 2.foo -> second foo syntax. from diku. */
  const char *a = argv[0];
  int num=1;
  if (argc) {
    while (*a && *a!='.') {
      if (!isdigit(*a)) {
	num=0;
      }
      a++;
    }
    if (num && *a=='.') {
      nth = atoi(argv[0])-1;

      if (!replaced_with) {
	replaced = 0;
	replaced_with = argv[0];
	all = 0;
      }

      argv[0] = a+1;
    }
  }

  string s;

  if (argc && !replaced_with) {
    const char *z = argv[argc-1];
    if (z[0] != '$') {
      /* deal with foo2 -> second foo */
      const char *x = z + strlen(z);
      while (x > z) {
	x--;
	if (!isdigit(*x)) {
	  x++;
	  break;
	}
      }
      if (isdigit(*x) && x != z && x[-1]!='_') {
	nth = atoi(x)-1;
	all = 0;
	s = argv[argc-1];
	s = s.substr(0, x-z);
	
	replaced = argc-1;
	replaced_with = argv[argc-1];
	argv[argc-1] = s.c_str();
      }
    }
  }

  if (argc && streq(argv[0], "them") && count==-1) {
    all = 1;
  }

  Matches mi = match(forwho, argc, argv, fn, flags, all_nothing, special);

  if ((!argc || !argv[0][0]) && !all) {
    Matches m;
    all_nothing = 103;

    if (replaced_with) {
      argv[replaced] = replaced_with;
    }

    return m;
  }

  if (replaced_with) {
    argv[replaced] = replaced_with;
  }

  if (!all) {
    Matches m2;

    if (count==-1 && (int)mi.size()>nth && nth>=0) {
      if (any) {
	nth = random_number(mi.size());
      }
      m2.push_back(mi[nth]);
      return m2;
    } else if (count==-1) {
      return m2;
    } else if (count) {
      if (count>(int)mi.size()) count = mi.size();

      for (int i=0;i<count;i++) {

	if ((int)mi.size()>i) {
	  m2.push_back(mi[i]);
	}
      }
    }
    return m2;
  }

  return mi;
}

static void onceadd(NewWorld &w, MudObject *o)
{
  if (!w.get(o->id)) {
    w.add(*o);
  }
}

static void onceadd(TeleWorld &sofar, TeleObject ob)
{
  if (!sofar.has(ob)) {
    sofar.add(ob);
  }
}

bool prep(const char *o) {
  if (streq(o, "in")) return 1;
  if (streq(o, "to")) return 1;
  if (streq(o, "from")) return 1;
  if (streq(o, "for")) return 1;
  if (streq(o, "with")) return 1;
  if (streq(o, "into")) return 1;
  if (streq(o, "onto")) return 1;
  if (streq(o, "on")) return 1;
  if (streq(o, "at")) return 1;
  return 0;
}

static void andmatch(MudObject *forwho, World<MudObject>*special, 
	       int argc, const char **argv, tfn_t fn, int flags, TeleWorld &sofar) {
  int st = 0;

  for (int i=0;i<argc;i++) {
    if (streq(argv[i], "and")) {
      Matches m2 = match2(forwho, i-st, argv+st, fn, flags, sofar.all_nothing, special);
      Matches::iterator it = m2.begin();
      while (it != m2.end()) {
	onceadd(sofar, it->ob);
	it++;
      }
      st = i+1;
    }
  }

  Matches m2 = match2(forwho, argc-st, argv+st, fn, flags, sofar.all_nothing, special);
  Matches::iterator it = m2.begin();
  while (it != m2.end()) {
    onceadd(sofar, it->ob);
    it++;
  }
}


TeleWorld multi_match(MudObject *forwho, int argc, const char **argv,
		   tfn_t fn, int flags, int *nexto, World<MudObject> *special, 
		   const char *prepf)
{
  TeleWorld sofar;

  sofar.all_nothing = 1;
  sofar.prep = NULL;

  if (prepf) {
    int i;
    for (i=0;i<argc;i++) {
      if (streq(argv[i], prepf)) {
	return multi_match(forwho, argc-i, argv+i, fn, flags, nexto, special);
      }
    }
    sofar.all_nothing = 200;
    return sofar;
  }

  if (nexto) {
    *nexto += argc;
  }
  
  if (prep(argv[0])) {
    sofar.prep = argv[0];
    argc--;
    argv++;
  }

  for (int i=1;i<argc;i++) {
    if (prep(argv[i])) {
      if (nexto) {
	*nexto += (i - argc);
      }
      argc = i;
    }
  }

  if (argc>=1)
    sofar.txt = argv[0];
  for (int i=1;i<argc;i++) {
    sofar.txt += " ";
    sofar.txt += argv[i];
  }

  int but = 0;
  for (int i=1;i<argc;i++) {
    if (streq(argv[i], "but")) {
      but = i;
    }
  }
  if (but) {
    TeleWorld all;
    TeleWorld except;
    andmatch(forwho, special, but, argv, fn, flags, all);
    andmatch(forwho, special, argc-but-1, argv+but+1, fn, flags|DEFAULT_ALL, except);

    for (int i=0;i<all.getsize();i++) {
      TeleObject ob = all.get(i);
      if (!except.has(ob))
	sofar.add(ob);
    }

    return sofar;
  }

  andmatch(forwho, special, argc, argv, fn, flags, sofar);

  return sofar;
}



NewWorld match(MudObject *forwho, int argc, const char **argv,
	       tfn_t fn, int flags, int *nexto, World<MudObject> *special,
	       const char *prepf)
{
  NewWorld sofar;

  sofar.prep = NULL;

  TeleWorld m=multi_match(forwho, argc, argv, fn, flags, nexto, special, prepf);

  for (int i=0;i<m.getsize();i++) {
    MudObject *o = m.get_nth(i);
    onceadd(sofar, o);
  }

  sofar.prep = m.prep;
  sofar.all_nothing = m.all_nothing;
  sofar.txt = m.txt;

  return sofar;
}


NewWorld match(MudObject *forwho, int argc, const char **argv,
	       tfn_t fn, int flags, const char *prep)
{
  return match(forwho, argc, argv, fn, flags, 0, 0, prep);
}