nakedmudv3.6/
nakedmudv3.6/lib/
nakedmudv3.6/lib/help/A/
nakedmudv3.6/lib/help/B/
nakedmudv3.6/lib/help/C/
nakedmudv3.6/lib/help/D/
nakedmudv3.6/lib/help/G/
nakedmudv3.6/lib/help/H/
nakedmudv3.6/lib/help/J/
nakedmudv3.6/lib/help/L/
nakedmudv3.6/lib/help/M/
nakedmudv3.6/lib/help/O/
nakedmudv3.6/lib/help/P/
nakedmudv3.6/lib/help/R/
nakedmudv3.6/lib/help/S/
nakedmudv3.6/lib/help/W/
nakedmudv3.6/lib/logs/
nakedmudv3.6/lib/misc/
nakedmudv3.6/lib/players/
nakedmudv3.6/lib/txt/
nakedmudv3.6/lib/world/
nakedmudv3.6/lib/world/examples/
nakedmudv3.6/lib/world/examples/mproto/
nakedmudv3.6/lib/world/examples/oproto/
nakedmudv3.6/lib/world/examples/reset/
nakedmudv3.6/lib/world/examples/rproto/
nakedmudv3.6/lib/world/examples/trigger/
nakedmudv3.6/lib/world/limbo/
nakedmudv3.6/lib/world/limbo/room/
nakedmudv3.6/lib/world/limbo/rproto/
nakedmudv3.6/src/alias/
nakedmudv3.6/src/dyn_vars/
nakedmudv3.6/src/editor/
nakedmudv3.6/src/example_module/
nakedmudv3.6/src/help2/
nakedmudv3.6/src/set_val/
nakedmudv3.6/src/socials/
nakedmudv3.6/src/time/
//*****************************************************************************
//
// parse.h
//
// Player commands usually each take their own strict format. For instance,
// the give command takes the format give <player> <object>. However, having to
// parse out these arguments by hand in each command is a bit of a hassle and
// something we can probably cut out most of the legwork for. That's what the
// functions in this header are designed to do.
//
//*****************************************************************************
#include "mud.h"
#include "utils.h"
#include "handler.h"
#include "inform.h"
#include "parse.h"

// mandatory modules
#include "scripts/scripts.h"
#include "scripts/pyexit.h"



//*****************************************************************************
// local datastructures, variables, and defines
//*****************************************************************************
#define PARSE_TOKEN_CHAR       0
#define PARSE_TOKEN_OBJ        1
#define PARSE_TOKEN_ROOM       2
#define PARSE_TOKEN_EXIT       3
#define PARSE_TOKEN_MULTI      4
#define PARSE_TOKEN_FLAVOR     5
#define PARSE_TOKEN_WORD       6
#define PARSE_TOKEN_DOUBLE     7
#define PARSE_TOKEN_INT        8
#define PARSE_TOKEN_BOOL       9
#define PARSE_TOKEN_OPTIONAL  10
#define PARSE_TOKEN_STRING    11

#define PARSE_VAR_BOOL         0
#define PARSE_VAR_INT          1
#define PARSE_VAR_DOUBLE       2
#define PARSE_VAR_CHAR         4
#define PARSE_VAR_OBJ          5
#define PARSE_VAR_ROOM         6
#define PARSE_VAR_EXIT         7
#define PARSE_VAR_STRING       8



//
// data for one format argument
typedef struct {
  int             type; // ch, obj, room, string, int, double, etc...?
  bitvector_t    scope; // what scope arguments are supplied to find?
  bool          all_ok; // is all. syntax allowed?
  bool         self_ok; // if we're looking for a char, can we find ourself?
  LIST     *token_list; // if we're a multi-type, a list of our possible format
  char         *flavor; // if we're a flavor type, the string we accept
  bool flavor_optional; // pretty self-explanatory :)
} PARSE_TOKEN;


//
// create a new parse token of the specified type
PARSE_TOKEN *newParseToken(int type) {
  PARSE_TOKEN *token = calloc(1, sizeof(PARSE_TOKEN));
  token->type = type;
  if(type == PARSE_TOKEN_MULTI)
    token->token_list = newList();
  else if(type == PARSE_TOKEN_OBJ)
    SET_BIT(token->scope, FIND_SCOPE_VISIBLE);
  else if(type == PARSE_TOKEN_EXIT) {
    SET_BIT(token->scope, FIND_SCOPE_VISIBLE);
    SET_BIT(token->scope, FIND_SCOPE_ROOM);
  }
  else if(type == PARSE_TOKEN_CHAR) {
    SET_BIT(token->scope, FIND_SCOPE_VISIBLE);
    token->self_ok = TRUE;
  }
  return token;
}


//
// free a token from memory
void deleteParseToken(PARSE_TOKEN *arg) {
  if(arg->token_list)
    deleteListWith(arg->token_list, deleteParseToken);
  if(arg->flavor)
    free(arg->flavor);
  free(arg);
}



//
// data for one parsed variable
typedef struct {
  int               type; // the type we parsed out
  bool          bool_val; // bool value
  int            int_val; // integer value
  double         dbl_val; // double value
  void          *ptr_val; // pointer value (room, ch, obj, exit)
  int disambiguated_type; // our parse type (in parse.h) if we were parsed 
                          // from a set of multiple possible types
  bool multiple_possible; // was it possible to parse multiple variables
  bool          multiple; // were multiple things parsed
} PARSE_VAR;


//
// create a new parse var of the specified type
PARSE_VAR *newParseVar(int type) {
  PARSE_VAR          *var = calloc(1, sizeof(PARSE_VAR));
  var->disambiguated_type = PARSE_NONE;
  var->type               = type;
  return var;
}  


//
// delete a parse var
void deleteParseVar(PARSE_VAR *var) {
  free(var);
}



//*****************************************************************************
// local functions
//*****************************************************************************

//
// makes a char token, and parses options. Returns NULL if there's bad options
PARSE_TOKEN *parse_token_char(const char *options) {
  // make it
  PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_CHAR);

  // search for options
  while(*options != '\0') {
    if(!strncasecmp(options, ".room", 5)) {
      options = options + 5;
      SET_BIT(token->scope, FIND_SCOPE_ROOM);
    }
    else if(!strncasecmp(options, ".world", 6)) {
      options = options + 6;
      SET_BIT(token->scope, FIND_SCOPE_WORLD);
    }
    else if(!strncasecmp(options, ".multiple", 9)) {
      options = options + 9;
      token->all_ok = TRUE;
    }
    else if(!strncasecmp(options, ".noself", 7)) {
      options = options + 7;
      token->self_ok = FALSE;
    }
    else if(!strncasecmp(options, ".invis_ok", 9)) {
      options = options + 9;
      REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
    }
    // didn't recognize the option
    else {
      deleteParseToken(token);
      token = NULL;
      break;
    }
  }

  // if we successfully parsed the token, make sure there's a scope to look in
  if(token != NULL && !IS_SET(token->scope, FIND_SCOPE_ROOM|FIND_SCOPE_WORLD)) {
    deleteParseToken(token);
    token = NULL;
  }

  return token;
}


//
// makes an obj token, and parses options. Returns NULL if there's bad options
PARSE_TOKEN *parse_token_obj(const char *options) {
  // make it
  PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_OBJ);

  // search for options
  while(*options != '\0') {
    if(!strncasecmp(options, ".room", 5)) {
      options = options + 5;
      SET_BIT(token->scope, FIND_SCOPE_ROOM);
    }
    else if(!strncasecmp(options, ".world", 6)) {
      options = options + 6;
      SET_BIT(token->scope, FIND_SCOPE_WORLD);
    }
    else if(!strncasecmp(options, ".inv", 4)) {
      options = options + 4;
      SET_BIT(token->scope, FIND_SCOPE_INV);
    }
    else if(!strncasecmp(options, ".eq", 3)) {
      options = options + 3;
      SET_BIT(token->scope, FIND_SCOPE_WORN);
    }
    else if(!strncasecmp(options, ".multiple", 9)) {
      options = options + 9;
      token->all_ok = TRUE;
    }
    else if(!strncasecmp(options, ".invis_ok", 9)) {
      options = options + 9;
      REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
    }
    // didn't recognize the option
    else {
      deleteParseToken(token);
      token = NULL;
      break;
    }
  }

  // if we successfully parsed the token, make sure there's a scope to look in
  if(token != NULL && !IS_SET(token->scope, FIND_SCOPE_ROOM | FIND_SCOPE_WORLD |
			                    FIND_SCOPE_INV  | FIND_SCOPE_WORN)){
    deleteParseToken(token);
    token = NULL;
  }

  return token;
}


//
// makes an exit token, and parses options. Returns NULL if there's bad options
PARSE_TOKEN *parse_token_exit(const char *options) {
  // make it
  PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_EXIT);

  // search for options
  while(*options != '\0') {
    if(!strncasecmp(options, ".multiple", 9)) {
      options = options + 9;
      token->all_ok = TRUE;
    }
    else if(!strncasecmp(options, ".invis_ok", 9)) {
      options = options + 9;
      REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
    }
    // didn't recognize the option
    else {
      deleteParseToken(token);
      token = NULL;
      break;
    }
  }

  return token;
}


//
// parses one actual thing (int, double, bool, string, ch, obj, room) marker.
// Does not understand { }, ( ), [ ], |, or *. Assumes no leading whitespaces.
PARSE_TOKEN *parse_one_datatype(const char **format) {
  char buf[SMALL_BUFFER];
  const char    *fmt = *format;
  PARSE_TOKEN *token = NULL;
  int i = 0;

  // copy what we're trying to parse
  for(i = 0; isalpha(*fmt) || *fmt == '_' || *fmt == '.'; i++, fmt++)
    buf[i] = *fmt;
  buf[i] = '\0';

  // figure out our type
  if(!strcasecmp(buf, "int"))
    token = newParseToken(PARSE_TOKEN_INT);
  else if(!strcasecmp(buf, "double"))
    token = newParseToken(PARSE_TOKEN_DOUBLE);
  else if(!strcasecmp(buf, "bool"))
    token = newParseToken(PARSE_TOKEN_BOOL);
  else if(!strcasecmp(buf, "word"))
    token = newParseToken(PARSE_TOKEN_WORD);
  else if(!strcasecmp(buf, "string"))
    token = newParseToken(PARSE_TOKEN_STRING);
  else if(!strcasecmp(buf, "room"))
    token = newParseToken(PARSE_TOKEN_ROOM);

  // exits, chars, and objs can have arguments tagged to the end of them. For
  // exits, it is optional. For both chars and objs, it is neccessary to specify
  // at least the scope of search (e.g. ch.room, obj.inv, obj.room.inv)
  else if(!strcasecmp(buf, "exit") || !strncasecmp(buf, "exit.", 5))
    token = parse_token_exit(buf+4);
  else if(!strncasecmp(buf, "ch.", 3))
    token = parse_token_char(buf+2);
  else if(!strncasecmp(buf, "obj.", 4))
    token = parse_token_obj(buf+3);

  // up the location of format if we successfully parsed something
  if(token != NULL)
    *format = fmt;

  // return whatever we found
  return token;
}


//
// parses a multi token assumes no leading whitespace
PARSE_TOKEN *parse_multi_token(const char **format) {
  const char *fmt = *format;
  bool close_found = FALSE;

  // skip our opening {
  fmt++;

  // skip any leading whitespace
  while(isspace(*fmt))
    fmt++;

  // make our multi-token
  PARSE_TOKEN *multi_token = newParseToken(PARSE_TOKEN_MULTI);

  // parse all of our tokens and add them to the multi-list
  while(*fmt != '\0' && !close_found) {
    PARSE_TOKEN *one_token = parse_one_datatype(&fmt);

    // did we parse the token alright?
    if(one_token != NULL) {
      listQueue(multi_token->token_list, one_token);
      // up the positioning of format
      *format = fmt;
    }
    // syntactic error. break out!
    else
      break;

    // skip any whitespace
    while(isspace(*fmt))
      fmt++;

    // have we encountered the closing bracket?
    if(*fmt == '}')
      close_found = TRUE;
  }

  // if we never found a close, or we didn't parse any arguments,
  // delete the token because it was not finished
  if(close_found == FALSE || listSize(multi_token->token_list) == 0) {
    deleteParseToken(multi_token);
    multi_token = NULL;
  }
  // otherwise, skip the closing bracket and up the position of format
  else {
    fmt++;
    *format = fmt;
  }

  return multi_token;
}


//
// parses a flavor token
PARSE_TOKEN *parse_flavor_token(const char **format, bool optional) {
  // make it
  char buf[SMALL_BUFFER];
  PARSE_TOKEN     *token = newParseToken(PARSE_TOKEN_FLAVOR);
  token->flavor_optional = optional;
  char       open_marker = (optional ? '[' : '<');
  char      close_marker = (optional ? ']' : '>');
  const char        *fmt = *format;
  int      i, open_count = 1;

  // skip the opening marker
  fmt++;

  // copy the flavor over to our buffer
  for(i = 0; *fmt != '\0'; i++, fmt++) {
    // did we encounter an open marker? if so, up our open count
    if(*fmt == open_marker)
      open_count++;
    // did we encounter a close marker? if so, lower our open count
    else if(*fmt == close_marker)
      open_count--;
    // did we just close everything off? if so, break out and skip last token
    if(open_count == 0) {
      fmt++;
      break;
    }
    buf[i] = *fmt;
  }
  buf[i] = '\0';

  // make sure we closed everything off
  if(open_count > 0) {
    deleteParseToken(token);
    token = NULL;
  }
  // move our format up and copy over the flavor string
  else {
    token->flavor = strdup(buf);
    *format = fmt;
  }

  return token;
}


//
// parse an optional marker
PARSE_TOKEN *parse_optional_token(const char **format) {
  // skip over the optional marker
  (*format)++;
  return newParseToken(PARSE_TOKEN_OPTIONAL);
}


//
// parses out one argument. Returns NULL if an error was encountered. If there
// was no error, ups the location of fmt to the end of the last argument parsed
PARSE_TOKEN *parse_one_token(const char **format) {
  const char    *fmt = *format;
  PARSE_TOKEN *token = NULL;

  // skip whitespaces
  while(isspace(*fmt))
    fmt++;

  // are we trying to parse multiple arguments?
  if(*fmt == '{')
    token = parse_multi_token(&fmt);

  // optional flavor format
  else if(*fmt == '[')
    token = parse_flavor_token(&fmt, TRUE);

  // mandatory flavor format
  else if(*fmt == '<')
    token = parse_flavor_token(&fmt, FALSE);

  // optional marker
  else if(*fmt == '|')
    token = parse_optional_token(&fmt);

  // a datatype token
  else
    token = parse_one_datatype(&fmt);

  // up the location of our format
  *format = fmt;
  
  return token;
}


//
// breaks up a format string into its parts and returns a list of them
LIST *decompose_parse_format(const char *format) {
  const char  *fmt = format; 
  bool       error = FALSE;
  LIST *token_list = newList();

  // try to parse all of our format down into tokens
  while(*fmt != '\0' && !error) {
    PARSE_TOKEN *token = parse_one_token(&fmt);

    // did the token parse OK?
    if(token != NULL)
      listQueue(token_list, token);
    else
      error = TRUE;
  }

  // did we encounter an error?
  if(error == TRUE) {
    deleteListWith(token_list, deleteParseToken);
    token_list = NULL;
  }

  return token_list;
}

//
// turns a string into a boolean
bool string_to_bool(const char *string) {
  if(!strcasecmp(string, "yes") || !strcasecmp(string, "true"))
    return TRUE;
  return FALSE;
}


//
// returns whether or not there is a boolean value available at the 
// head of the string.
bool string_is_bool(const char *string) {
  return (!strcasecmp(string, "yes") || !strcasecmp(string, "true")  || 
	  !strcasecmp(string, "no")  || !strcasecmp(string, "false"));
}


//
// returns whether or not the string is a double value
bool string_is_double(const char *string) {
  bool deci_found = FALSE;
  int     len = 0;
  // check if it's negative
  if(*string == '-')
    string++;
  // it's a double if we only encounter digits and a single dicimal
  for(; *string != '\0'; string++, len++) {
    if(isdigit(*string))
      continue;
    // only allowed decimals, and only one of them...
    else if(*string != '.' || deci_found == TRUE)
      return FALSE;
    deci_found = TRUE;
  }
  // found no problems, and actually found _something_
  return (len > 0);
}


//
// returns whether or not the string is an integer
bool string_is_int(const char *string) {
  int len = 0;
  // check if it's a negative
  if(*string == '-')
    string++;
  // it's an int if all we encounter is digits
  for(; *string != '\0'; string++, len++)
    if(!isdigit(*string))
      return FALSE;
  // found no problems, and actually found _something_
  return (len > 0);
}


//
// tries to make an int parse var
PARSE_VAR *use_one_parse_token_int(const char *buf) {
  PARSE_VAR *var = NULL;
  if(string_is_int(buf)) {
    var = newParseVar(PARSE_VAR_INT);
    var->int_val = atoi(buf);
  }
  return var;
}


//
// tries to make a double parse var
PARSE_VAR *use_one_parse_token_double(const char *buf) {
  PARSE_VAR *var = NULL;
  if(string_is_double(buf)) {
    var = newParseVar(PARSE_VAR_DOUBLE);
    var->dbl_val = atof(buf);
  }
  return var;
}


//
// tries to make a boolean parse var
PARSE_VAR *use_one_parse_token_bool(const char *buf) {
  PARSE_VAR *var = NULL;
  if(string_is_bool(buf)) {
    var = newParseVar(PARSE_VAR_BOOL);
    var->bool_val = string_to_bool(buf);
  }
  return var;
}


//
// tries to make a char parse var
PARSE_VAR *use_one_parse_token_char(CHAR_DATA *looker, PARSE_TOKEN *tok,
				    const char *name) {
  int    type = FOUND_NONE;
  void *found = generic_find(looker, name, FIND_TYPE_CHAR, tok->scope,
			     tok->all_ok, &type);

  // make sure we found something...
  if(found == NULL)
    return NULL;
  else {
    PARSE_VAR *var = newParseVar(PARSE_VAR_CHAR);

    // if multiple vals were possible, flag it
    var->multiple_possible = tok->all_ok;

    // make sure it's not us if that's not allowed
    if(type == FOUND_CHAR) {
      if(tok->self_ok || looker != found)
	var->ptr_val = found;
      else {
	deleteParseVar(var);
	var = NULL;
      }
    }
    // if we got a list, make sure we remove ourself as neccessary
    else if(type == FOUND_LIST) {
      if(!tok->self_ok)
	listRemove(found, looker);
      // make sure we're not empty...
      if(listSize(found) > 1) {
	var->ptr_val  = found;
	var->multiple = TRUE;
      }
      else if(listSize(found) == 1) {
	var->ptr_val = listPop(found);
	deleteList(found);
      }
      else {
	deleteList(found);
	deleteParseVar(var);
	var = NULL;
      }
    }
    // this shouldn't happen...
    else {
      deleteParseVar(var);
      var = NULL;
    }

    // return what we found, if anything
    return var;
  }
}


//
// tries to make an obj parse var
PARSE_VAR *use_one_parse_token_obj(CHAR_DATA *looker, PARSE_TOKEN *tok,
				   const char *name) {
  int    type = FOUND_NONE;
  void *found = generic_find(looker, name, FIND_TYPE_OBJ, tok->scope,
			     tok->all_ok, &type);

  // make sure we found something
  if(found == NULL)
    return NULL;
  else {
    PARSE_VAR *var = newParseVar(PARSE_VAR_OBJ);

    // if multiple vals were possible, flag it
    var->multiple_possible = tok->all_ok;

    // Is it a single item?
    if(type == FOUND_OBJ)
      var->ptr_val = found;

    // or is it multiple items?
    else if(type == FOUND_LIST) {
      if(listSize(found) > 1) {
	var->ptr_val = found;
	var->multiple = TRUE;
      }
      else if(listSize(found) == 1) {
	var->ptr_val = listPop(found);
	deleteList(found);
      }
      else {
	deleteList(found);
	deleteParseVar(var);
	var = NULL;
      }
    }

    // We should never reach this case
    else {
      deleteParseVar(var);
      var = NULL;
    }

    // return whatever we found
    return var;
  }
}


//
// tries to make an exit parse var
PARSE_VAR *use_one_parse_token_exit(CHAR_DATA *looker, PARSE_TOKEN *tok, 
				    const char *name) {
  int    type = FOUND_NONE;
  void *found = generic_find(looker, name, FIND_TYPE_EXIT, tok->scope,
			     tok->all_ok, &type);

  // make sure we found something
  if(found == NULL)
    return NULL;
  else {
    PARSE_VAR *var = newParseVar(PARSE_VAR_EXIT);

    // if multiple vals were possible, flag it
    var->multiple_possible = tok->all_ok;

    // Is it a single exit?
    if(type == FOUND_EXIT)
      var->ptr_val = found;

    // or is it multiple exits?
    else if(type == FOUND_LIST) {
      if(listSize(found) > 1) {
	var->ptr_val = found;
	var->multiple = TRUE;
      }
      else if(listSize(found) == 1) {
	var->ptr_val = listPop(found);
	deleteList(found);
      }
      else {
	deleteList(found);
	deleteParseVar(var);
	var = NULL;
      }
    }

    // We should never reach this case
    else {
      deleteParseVar(var);
      var = NULL;
    }

    // return whatever we found
    return var;
  }
}


//
// tries to make a room parse var
PARSE_VAR *use_one_parse_token_room(CHAR_DATA *looker, PARSE_TOKEN *tok,
				    const char *name) {
  ROOM_DATA *room = generic_find(looker, name, FIND_TYPE_ROOM, FIND_SCOPE_WORLD,
				 FALSE, NULL);

  // did we find something?
  if(room == NULL)
    return NULL;
  else {
    PARSE_VAR *var = newParseVar(PARSE_VAR_ROOM);
    var->ptr_val   = room;
    return var;
  }
}


//
// Tries to apply a token to args, to parse something out. Returns a PARSE_VAR
// if the token's usage resulted in the creation of a variable. If an error was
// encountered, make this apparent by setting the value of the variable at error
PARSE_VAR *use_one_parse_token(CHAR_DATA *looker, PARSE_TOKEN *tok, 
			       char **args, bool *error, char *err_buf) {
  char buf[SMALL_BUFFER];
  PARSE_VAR  *var = NULL;
  char       *arg = *args;

  // skip over any leading spaces we might have
  while(isspace(*arg))
    arg++;

  switch(tok->type) {
  case PARSE_TOKEN_MULTI: {
    // make a proxy error value and error buf so we don't accidentally fill
    // these up when it turns out we find something on a var past the first
    bool multi_err = FALSE;
    char multi_err_buf[SMALL_BUFFER] = "";

    // go through all of our possible types until we find something
    LIST_ITERATOR *multi_i = newListIterator(tok->token_list);
    PARSE_TOKEN      *mtok = NULL;
    bool multiple_possible = FALSE;
    ITERATE_LIST(mtok, multi_i) {
      if(mtok->all_ok)
	multiple_possible = TRUE;
      var = use_one_parse_token(looker, mtok, &arg, &multi_err, multi_err_buf);
      // reset our error value for the next pass at it...
      if(var == NULL)
	multi_err = FALSE;
      // found something! Disambiguate the type
      else {
	switch(mtok->type) {
	case PARSE_TOKEN_CHAR:    var->disambiguated_type = PARSE_CHAR;   break;
	case PARSE_TOKEN_ROOM:    var->disambiguated_type = PARSE_ROOM;   break;
	case PARSE_TOKEN_EXIT:    var->disambiguated_type = PARSE_EXIT;   break;
	case PARSE_TOKEN_OBJ:     var->disambiguated_type = PARSE_OBJ;    break;
	case PARSE_TOKEN_WORD:    var->disambiguated_type = PARSE_STRING; break;
	case PARSE_TOKEN_STRING:  var->disambiguated_type = PARSE_STRING; break;
	case PARSE_TOKEN_INT:     var->disambiguated_type = PARSE_INT;    break;
	case PARSE_TOKEN_DOUBLE:  var->disambiguated_type = PARSE_DOUBLE; break;
	case PARSE_TOKEN_BOOL:    var->disambiguated_type = PARSE_BOOL;   break;
	}
	// break out of the loop... we found something
	break;
      }
    } deleteListIterator(multi_i);

    // did we manage not to find something?
    if(var != NULL)
      var->multiple_possible = multiple_possible;
    else {
      one_arg(arg, buf); // get the first arg, for reporting...
      sprintf(err_buf, "Your argument '%s' was invalid or could not be found.",
	      buf);
      *error = TRUE;
    }
    break;
  }

  case PARSE_TOKEN_FLAVOR: {
    int len = strlen(tok->flavor);
    // have we found the flavor text?
    if(strncasecmp(tok->flavor, arg, len) == 0 &&
	 (arg[len] == '\0' || isspace(arg[len])))
      arg = arg + len;
    // do we need to do something about it?
    else if(!tok->flavor_optional)
      *error = TRUE;
    break;
  }

    // parse out a char value
  case PARSE_TOKEN_CHAR:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_char(looker, tok, buf);
    if(var == NULL) {
      sprintf(err_buf, "Could not find person, '%s'.", buf);
      *error = TRUE;
    }
    break;

    // parse out an obj value
  case PARSE_TOKEN_OBJ:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_obj(looker, tok, buf);
    if(var == NULL) {
      sprintf(err_buf, "Could not find object, '%s'.", buf);
      *error = TRUE;
    }
    break;

    // parse out a room value
  case PARSE_TOKEN_ROOM:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_room(looker, tok, buf);
    if(var == NULL) {
      sprintf(err_buf, "Could not find room, '%s'.", buf);
      *error = TRUE;
    }
    break;

    // parse out an exit value
  case PARSE_TOKEN_EXIT:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_exit(looker, tok, buf);
    if(var == NULL) {
      sprintf(err_buf, "Could not find door or direction named '%s'.", buf);
      *error = TRUE;
    }
    break;

    // try to parse out a double value
  case PARSE_TOKEN_DOUBLE:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_double(buf);
    if(var == NULL) {
      sprintf(err_buf, "'%s' is not a decimal value.", buf);
      *error = TRUE;
    }
    break;

    // try to parse out an integer value
  case PARSE_TOKEN_INT:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_int(buf);
    if(var == NULL) {
      sprintf(err_buf, "'%s' is not a%s number.", buf,
	      (string_is_double(buf) ? "n acceptable" : ""));
      *error = TRUE;
    }
    break;

    // try to parse out a boolean value
  case PARSE_TOKEN_BOOL:
    arg = one_arg(arg, buf);
    var = use_one_parse_token_bool(buf);
    if(var == NULL) {
      sprintf(err_buf, "'%s' is not a yes/no value.", buf);
      *error = TRUE;
    }
    break;

    // parse out a single word
  case PARSE_TOKEN_WORD: {
    var = newParseVar(PARSE_VAR_STRING);
    var->ptr_val    = arg;
    bool multi_word = FALSE;

    // are we using quotation marks to specify multiple words?
    if(*arg == '\"') {
      multi_word = TRUE;
      arg++;
      var->ptr_val = arg;
    }

    // go through arg to the next space, and delimit the word
    for(; *arg != '\0'; arg++) {
      if((multi_word && *arg == '\"') || (!multi_word && isspace(*arg))) {
	*arg = '\0';
	arg++;
	break;
      }
    }
    break;
  }

    // copies whatever is left
  case PARSE_TOKEN_STRING:
    var = newParseVar(PARSE_VAR_STRING);
    var->ptr_val = arg;
    // skip up the place of the arg...
    while(*arg != '\0')
      arg++;
    break;

    // since this doesn't really parse a value...
  case PARSE_TOKEN_OPTIONAL:
    break;
  }

  // up the placement of our arg if we didn't encounter an error
  if(!*error)
    *args = arg;

  return var;
}


//
// gets the name of the type for printing in syntax error messages
const char *get_datatype_format_error_mssg(PARSE_TOKEN *tok) {
  switch(tok->type) {
  case PARSE_TOKEN_CHAR:
    return "person";
  case PARSE_TOKEN_ROOM:
    return "room";
  case PARSE_TOKEN_EXIT:
    return "direction";
  case PARSE_TOKEN_OBJ:
    return "object";
  case PARSE_TOKEN_INT:
    return "number";
  case PARSE_TOKEN_DOUBLE:
    return "decimal";
  case PARSE_TOKEN_BOOL:
    return "yes/no";
  case PARSE_TOKEN_WORD:
    return "word";
  case PARSE_TOKEN_STRING:
    return "text";
  default:
    return "UNKNOWN_TYPE";
  }
}


//
// Takes a list of tokens, and builds the proper syntax for the command and
// then sends it to the character.
void show_parse_syntax_error(CHAR_DATA *ch, const char *cmd, LIST *tokens) {
  BUFFER            *buf = newBuffer(1);
  LIST_ITERATOR   *tok_i = newListIterator(tokens);
  PARSE_TOKEN       *tok = NULL;
  bool    optional_found = FALSE;
  int              count = 0;

  // go through all of our tokens, and append their syntax to the buf
  ITERATE_LIST(tok, tok_i) {
    // make sure we add a space before anything else...
    if(count > 0)
      bprintf(buf, " ");
    count++;

    // have we encountered the "optional" marker? if so, switch our open/close
    if(tok->type == PARSE_TOKEN_OPTIONAL) {
      bprintf(buf, "[");
      optional_found = TRUE;
      // we don't want to put a space right after this [
      count = 0;
      continue;
    }

    // append our message
    switch(tok->type) {
    case PARSE_TOKEN_MULTI: {
      bprintf(buf, "<");
      LIST_ITERATOR *multi_i = newListIterator(tok->token_list);
      PARSE_TOKEN      *mtok = NULL;
      int            m_count = 0;
      ITERATE_LIST(mtok, multi_i) {
	if(m_count > 0)
	  bprintf(buf, ", ");
	m_count++;
	bprintf(buf, "%s", get_datatype_format_error_mssg(mtok));
      } deleteListIterator(multi_i);
      bprintf(buf, ">");
      break;
    }

    case PARSE_TOKEN_FLAVOR:
      bprintf(buf, "%s%s%s", (tok->flavor_optional ? "[" : ""), tok->flavor,
	      (tok->flavor_optional ? "]" : ""));
      break;

    default:
      bprintf(buf, "<%s>", get_datatype_format_error_mssg(tok));
      break;
    }
  } deleteListIterator(tok_i);

  // send the message
  send_to_char(ch, "Proper syntax is: %s %s%s\r\n", cmd, bufferString(buf),
	       (optional_found ? "]" : ""));
  deleteBuffer(buf);
}


//
// builds up a list of variables out of the token list, and arguments. If we
// encounter an error and we need to show it to the looker, do so.
LIST *compose_variable_list(CHAR_DATA *looker, LIST *tokens, char *args,
			    char *err_buf) {
  LIST      *variables = newList();
  LIST_ITERATOR *tok_i = newListIterator(tokens);
  PARSE_TOKEN     *tok = NULL;
  bool           error = FALSE;
  bool  optional_found = FALSE;

  // go through our list of tokens, and try dealing with the args
  ITERATE_LIST(tok, tok_i) {
    PARSE_VAR *var = NULL;

    // did we just encounter an optional value?
    if(tok->type == PARSE_TOKEN_OPTIONAL) {
      optional_found = TRUE;
      continue;
    }
    
    // can we still use tokens to process stuff?
    if(*args != '\0')
      var = use_one_parse_token(looker, tok, &args, &error, err_buf);
    // we haven't found an "optional" marker yet - this isn't allowed
    else if(optional_found == FALSE)
      error = TRUE;
    // we have found an "optional" marker. Just break out of the loop
    else 
      break;

    // if use of the token returned a new variable, append it
    if(var != NULL)
      listQueue(variables, var);
    // if we enountered an error, tell the person if neccessary
    else if(error == TRUE) {
      deleteListWith(variables, deleteParseVar);
      variables = NULL;
      break;
    }
  } deleteListIterator(tok_i);

  return variables;
}


//
// Goes through the list of variables and fills up vargs with them as needed
void parse_assign_vars(LIST *variables, va_list vargs) {
  LIST_ITERATOR *var_i = newListIterator(variables);
  PARSE_VAR   *one_var = NULL;

  // go through each variable and assign to vargs as needed
  ITERATE_LIST(one_var, var_i) {
    // first, do our basic type
    switch(one_var->type) {
    case PARSE_VAR_BOOL:
      *va_arg(vargs, bool *) = one_var->bool_val;
      break;
    case PARSE_VAR_INT:
      *va_arg(vargs, int *) = one_var->int_val;
      break;
    case PARSE_VAR_DOUBLE:
      *va_arg(vargs, double *) = one_var->dbl_val;
      break;
    case PARSE_VAR_STRING:
    case PARSE_VAR_OBJ:
    case PARSE_VAR_CHAR:
    case PARSE_VAR_EXIT:
    case PARSE_VAR_ROOM:
      *va_arg(vargs, void **) = one_var->ptr_val;
      break;
    // this should never happen...
    default:
      break;
    }
    
    // now see if we have a multi_type
    if(one_var->disambiguated_type != PARSE_NONE)
      *va_arg(vargs, int *) = one_var->disambiguated_type;

    // and if we parsed multiple occurences
    if(one_var->multiple_possible == TRUE)
      *va_arg(vargs, bool *) = one_var->multiple;
  } deleteListIterator(var_i);
}


//
// Goes through the list of variables and make a python list with them
PyObject *parse_create_py_vars(LIST *variables) {
  LIST_ITERATOR *var_i = newListIterator(variables);
  PARSE_VAR   *one_var = NULL;
  PyObject       *list = PyList_New(0);
  PyObject      *pyval = NULL;

  // go through each variable and assign to vargs as needed
  ITERATE_LIST(one_var, var_i) {
    // first, do our basic type
    switch(one_var->type) {
    case PARSE_VAR_BOOL:
      pyval = Py_BuildValue("b", one_var->bool_val);
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
      break;
    case PARSE_VAR_INT:
      pyval = Py_BuildValue("i", one_var->int_val);
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
      break;
    case PARSE_VAR_DOUBLE:
      pyval = Py_BuildValue("f", one_var->dbl_val);
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
      break;
    case PARSE_VAR_STRING:
      pyval = Py_BuildValue("s", one_var->ptr_val);
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
      break;
    case PARSE_VAR_OBJ:
      if(one_var->multiple == FALSE)
	PyList_Append(list, objGetPyFormBorrowed(one_var->ptr_val));
      else {
	pyval = PyList_fromList(one_var->ptr_val, objGetPyForm);
	PyList_Append(list, pyval);
	Py_DECREF(pyval);
      }
      break;
    case PARSE_VAR_CHAR:
      if(one_var->multiple == FALSE)
	PyList_Append(list, charGetPyFormBorrowed(one_var->ptr_val));
      else {
	pyval = PyList_fromList(one_var->ptr_val, charGetPyForm);
	PyList_Append(list, pyval);
	Py_DECREF(pyval);
      }
      break;
    case PARSE_VAR_EXIT:
      pyval = newPyExit(one_var->ptr_val);
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
      break;
    case PARSE_VAR_ROOM:
      PyList_Append(list, roomGetPyFormBorrowed(one_var->ptr_val));
      break;
    // this should never happen...
    default:
      break;
    }
    
    // now see if we have a multi_type
    if(one_var->disambiguated_type != PARSE_NONE) {
      pyval = 
	Py_BuildValue("s", (one_var->disambiguated_type==PARSE_CHAR?"char":
			    (one_var->disambiguated_type==PARSE_ROOM?"room":
			     (one_var->disambiguated_type==PARSE_OBJ?"obj":
			      (one_var->disambiguated_type==PARSE_EXIT?"exit":
			       NULL)))));
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
    }

    // and if we parsed multiple occurences
    if(one_var->multiple_possible == TRUE) {
      pyval = Py_BuildValue("b", one_var->multiple);
      PyList_Append(list, pyval);
      Py_DECREF(pyval);
    }
  } deleteListIterator(var_i);

  return list;
}



//*****************************************************************************
// implementation of parse.h
//*****************************************************************************
int parse_expected_py_args(const char *syntax) {
  LIST *tokens = NULL;
  if((tokens = decompose_parse_format(syntax)) == NULL)
    return -1;
  int count = 0;
  LIST_ITERATOR *token_i = newListIterator(tokens);
  PARSE_TOKEN     *token = NULL;
  ITERATE_LIST(token, token_i) {
    if(token->type == PARSE_TOKEN_FLAVOR || token->type == PARSE_TOKEN_OPTIONAL)
      continue;

    // one for a returnable type
    count++;

    // one to denote what kind in an ambiguous case
    if(token->type == PARSE_TOKEN_MULTI)
      count++;

    // one to denote if we had multiples
    if(token->all_ok)
      count++;
  } deleteListIterator(token_i);
  deleteListWith(tokens, deleteParseToken);

  return count;
}

void *Py_parse_args(CHAR_DATA *looker, bool show_errors, const char *cmd, 
		    char *args, const char *syntax) {
  char err_buf[SMALL_BUFFER] = "";
  bool       parse_ok = TRUE;
  LIST        *tokens = NULL;
  LIST     *variables = NULL;
  PyObject      *list = NULL;

  // get our list of tokens
  if((tokens = decompose_parse_format(syntax)) == NULL) {
    log_string("Command '%s', format error in argument parsing: %s",cmd,syntax);
    parse_ok = FALSE;
  }
  // try to use our tokens to compose a variable list
  else if((variables = compose_variable_list(looker, tokens, args, err_buf))
	  == NULL)
    parse_ok = FALSE;
  else {
    // go through all of our vars and make python forms for them
    list = parse_create_py_vars(variables);

    // fill up optional spots at the end we didn't parse args for
    int expected = parse_expected_py_args(syntax);
    while(PyList_Size(list) < expected)
      PyList_Append(list, Py_None);
  }

  // did we encounter an error with the arguments and need to mssg someone?
  if(tokens != NULL && !parse_ok && show_errors) {
    // do we have a specific error message?
    if(*err_buf)
      send_to_char(looker, "%s\r\n", err_buf);
    // assume a syntax error
    else
      show_parse_syntax_error(looker, cmd, tokens);
  }

  // clean up our mess
  if(tokens != NULL)
    deleteListWith(tokens, deleteParseToken);
  if(variables != NULL)
    deleteListWith(variables, deleteParseVar);

  // return our parse status
  return list;
}

bool parse_args(CHAR_DATA *looker, bool show_errors, const char *cmd,
		char *args, const char *syntax, ...) {
  char err_buf[SMALL_BUFFER] = "";
  bool       parse_ok = TRUE;
  LIST       *tokens  = NULL;
  LIST     *variables = NULL;

  // get our list of tokens
  if((tokens = decompose_parse_format(syntax)) == NULL) {
    log_string("Command '%s', format error in argument parsing: %s",cmd,syntax);
    parse_ok = FALSE;
  }
  // try to use our tokens to compose a variable list
  else if((variables = compose_variable_list(looker, tokens, args, err_buf))
	  == NULL)
    parse_ok = FALSE;
  else {
    // go through all of our vars and assign them to the proper args
    va_list vargs;
    va_start(vargs, syntax);
    parse_assign_vars(variables, vargs);
    va_end(vargs);
  }

  // did we encounter an error with the arguments and need to mssg someone?
  if(tokens != NULL && !parse_ok && show_errors) {
    // do we have a specific error message?
    if(*err_buf)
      send_to_char(looker, "%s\r\n", err_buf);
    // assume a syntax error
    else
      show_parse_syntax_error(looker, cmd, tokens);
  }

  // clean up our mess
  if(tokens != NULL)
    deleteListWith(tokens, deleteParseToken);
  if(variables != NULL)
    deleteListWith(variables, deleteParseVar);

  // return our parse status
  return parse_ok;
}