/******************************************************************************
 Snippet: Dynamic description generator.
 Author:  Richard Woolcock (aka KaVir).
 Date:    14th October 1999.
 ******************************************************************************
 This code is copyright (C) 1999 by Richard Woolcock.  It may be used and
 distributed freely, as long as you don't remove this copyright notice.
 ******************************************************************************/

/******************************************************************************
 Please note that I have not actually bothered to tidy this code up yet 
 (assuming I ever will).  The calculator and text-replacement code were created
 separately for my own amusement, and I decided to stick them together so that
 I could create dynamic help files.  In retrospect the calculator should have 
 been written using tokens (each token having a priority).  However because 
 this code was only really ever intended for my own use, I decided not to 
 bother rewriting it (I'm only writing this comment because I know that sooner 
 or later this code will get out).  So in summary, make sure you put brackets
 around EVERYTHING.  If you want to check if someone is either a Caitiff or a
 Brujah, or example, write it like this:

 [({clan} == {caitiff}) || ({clan} == {brujah})]
    You're a {clan}!
 |

 You'll also need some sort of text justification function.  If you don't want 
 to write your own, I suggest grabbing a copy from an OLC (format_string or 
 whatever that OLC calls it) and replacing the str_dup'd string with a 
 pointer to an internal static char array.  You'll probably also want to add
 a newline option to it as well.

 To add new variables to the table, use the existing format.  Note that 
 there are four types of variable:

 VARIABLE_FUNCTION_STRING: Functions returning strings.
 VARIABLE_CONSTANT_STRING: Constant strings.
 VARIABLE_FUNCTION_NUMBER: Functions returning numbers.
 VARIABLE_CONSTANT_NUMBER: Constant numbers.

 Only the strings may be used within general text replacement.  Only the 
 numbers may be used within expressions.  You may have a string variable 
 and a number variable of the same name (in fact I recommend you do this).
 That's how the above example (with {clan}) would work - {clan} string 
 returns the name of the person's clan as a string, while the {clan} number 
 returns a number appropriate to that particular clan.

 Note that when compiled this may throw up warnings, because of the way 
 the table is constructed.

 I've left my functions in here as examples.  You'll almost definitely 
 want to remove them.  Have fun!  KaVir.
 ******************************************************************************/

/******************************************************************************
 You have to add the following prototypes to an appropriate header file:

 void	io_parse	args( ( CHAR_DATA *ch, char *in_ptr, char *out_ptr ) );
 void	io_display	args( ( CHAR_DATA *ch, char text[] ) );
 void	io_main		args( ( CHAR_DATA *ch ) );
 int	io_calculate	args( ( CHAR_DATA *ch, char *calculation ) );
 ******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include "merc.h"

typedef enum
{
   VARIABLE_FUNCTION_STRING,
   VARIABLE_CONSTANT_STRING,
   VARIABLE_FUNCTION_NUMBER,
   VARIABLE_CONSTANT_NUMBER
} VARIABLE_TYPE;

typedef enum
{
   STATE_NONE,
   STATE_LEFT_SIGN,
   STATE_LEFT,
   STATE_DONE_LEFT,
   STATE_OPERATOR,
   STATE_RIGHT_SIGN,
   STATE_RIGHT
} STATE_TYPE;

typedef union
{
   char* text;
   int   number;
} replace_type;

typedef struct
{
   const char * variable;
   const VARIABLE_TYPE type;
   replace_type replace;
} var_type;

/*********************************************************/
/*                 FUNCTION PROTOTYPES                   */
/*********************************************************/

static int	main_calc	( CHAR_DATA *ch, char calc_string[] );
bool		string_var	( char *var_ptr, char **out_ptr );
bool		number_var	( char *var_ptr, char *out_ptr );
static void	setup_variables	( CHAR_DATA *ch );

/*********************************************************/
/*                 VARIABLE FUNCTIONS                    */
/*********************************************************/

CHAR_DATA *you = NULL;
OBJ_DATA  *wpn = NULL;

/*********************************************************************/
/**************************** PURE STRING ****************************/
/*********************************************************************/

char *get_name( void ) { return ( you->name ); }

char *get_firstname( void ) { return ( you->forename ); }

char *get_surname( void ) { return ( you->surname ); }

char *get_fullname( void )
{
   static char buf [MAX_INPUT_LENGTH];
   strcpy( buf, you->forename );
   strcat( buf, " " );
   strcat( buf, you->surname );
   return ( buf );
}

char *get_he_she( void )  { return ( IS_FEMALE(you) ? "she" : "he"  ); }

char *get_him_her( void ) { return ( IS_FEMALE(you) ? "her" : "him" ); }

char *get_his_her( void ) { return ( IS_FEMALE(you) ? "her" : "his" ); }

char *obj_where_text( OBJ_DATA *obj )
{
   static char buf [MAX_INPUT_LENGTH];
   extern char * const male_where_name[];
   extern char * const female_where_name[];

   if ( !obj ) return "";

   if ( obj->in_obj )
   {
      sprintf( buf, "inside his %s", short_obj_desc(you,obj->in_obj,FALSE) );
      return buf;
   }
   if ( obj->carried_by == you )
   {
      if ( obj->wear_loc >= WEAR_LIGHT && obj->wear_loc < MAX_WEAR )
      {
         if ( IS_FEMALE(you) )
            sprintf( buf, female_where_name[obj->wear_loc], "X", "her" );
         else
            sprintf( buf, male_where_name[obj->wear_loc], "X", "his" );
         return &buf[2];
      }
   }
   return "";
}

/*********************************************************************/
/**************************** PURE NUMBER ****************************/
/*********************************************************************/

int get_class( void ) { return ( you->class ); }

int get_dot_mode( void ) { return (!IS_NPC(you) && IS_SET(you->act, PLR_DOT)); }

/*********************************************************************/
/********************** BOTH STRING AND NUMBER ***********************/
/*********************************************************************/

int   get_wpn( void )       { return ( !!wpn ); }
char *get_wpn_text( void )
	{ return ( wpn ? short_obj_desc(you,wpn,FALSE) : "fist" ); }
int   wpn_diff( void )      { return ( wpn ? wpn->value[1] : 0 ); }
char *wpn_diff_text( void ) { return ( number_text(wpn_diff()) ); }
int   wpn_dam( void )       { return ( wpn ? wpn->value[2] : 0 ); }
char *wpn_dam_text( void )  { return ( number_text(wpn_dam()) ); }
int   wpn_where( void )      { return ( wpn ? wpn->wear_loc : -1 ); }
char *wpn_where_text( void ) { return ( obj_where_text( wpn ) ); }

int get_generation( void )
{ return ( is_affected(you,gsn_potency) ? (you->vampgen + 1) : you->vampgen ); }
char *get_generation_text( void )
{
   switch ( you->vampgen )
   {
      default: return "bugged";
      case 15: return "15th";
      case 14: return "14th";
      case 13: return "13th";
      case 12: return "12th";
      case 11: return "11th";
      case 10: return "10th";
      case  9: return "9th";
      case  8: return "8th";
      case  7: return "7th";
      case  6: return "6th";
      case  5: return "5th";
      case  4: return "4th";
      case  3: return "3rd";
      case  2: return "2nd";
      case  1: return "1st";
   }
}

int get_max_disc( void )
{
   switch ( you->vampgen )
   {
      case 15: return 3;
      case 14: return 4;
      default: return 5;
      case 7:  return 6;
      case 6:  return 7;
      case 5:  return 8;
      case 4:  return 9;
      case 3:  return 10;
      case 2:  return 10;
      case 1:  return 10;
   }
}
char *get_max_disc_text( void )
{
   switch ( you->vampgen )
   {
      case 15: return "three";
      case 14: return "four";
      default: return "five";
      case  7: return "six";
      case  6: return "seven";
      case  5: return "eight";
      case  4: return "nine";
      case  3: return "ten";
      case  2: return "ten";
      case  1: return "ten";
   }
}

int get_sect( void ) { return ( find_sectname( you ) ); }
char *get_sect_text( void )
{
   return ( strlen(you->side) < 2 ? "None" : you->side );
}

int get_clan( void ) { return ( find_clanname( you ) ); }
char *get_clan_text( void )
{
   return ( strlen(you->clan) < 2 ? "Caitiff" : you->clan );
}

int get_clandisc1( void )
{ return ( IS_NPC(you) ? -1 : you->pcdata->cland[0] ); }
char *get_clandisc1_text( void )
{ return ( get_clandisc1() < 0 ? "None" : disc_name(get_clandisc1()) ); }

int get_clandisc2( void )
{ return ( IS_NPC(you) ? -1 : you->pcdata->cland[1] ); }
char *get_clandisc2_text( void )
{ return ( get_clandisc2() < 0 ? "None" : disc_name(get_clandisc2()) ); }

int get_clandisc3( void )
{ return ( IS_NPC(you) ? -1 : you->pcdata->cland[2] ); }
char *get_clandisc3_text( void )
{ return ( get_clandisc3() < 0 ? "None" : disc_name(get_clandisc3()) ); }

int get_conscience( void )
{ return ( you->virtues[1] ); }
char *get_conscience_text( void )
{
   switch ( you->virtues[1] )
   {
      default: return "bugged";
      case  1: return "Chaste";
      case  2: return "Moral";
      case  3: return "Just";
      case  4: return "Charitable";
      case  5: return "Remorseful";
   }
}

int get_selfcontrol( void )
{ return ( you->virtues[2] ); }
char *get_selfcontrol_text( void )
{
   switch ( you->virtues[2] )
   {
      default: return "bugged";
      case  1: return "Calm";
      case  2: return "Temperate";
      case  3: return "Disciplined";
      case  4: return "Hardened";
      case  5: return "Total self-mastery";
   }
}

int get_courage( void )
{ return ( you->virtues[3] ); }
char *get_courage_text( void )
{
   switch ( you->virtues[3] )
   {
      default: return "bugged";
      case  1: return "Bold";
      case  2: return "Dauntless";
      case  3: return "Steadfast";
      case  4: return "Gallant";
      case  5: return "Valorous";
   }
}

char *get_attribute_str_text(void) { return number_text(you->stats[STAT_STR]); }
int   get_attribute_str(void)      { return you->stats[STAT_STR]; }
char *get_attribute_dex_text(void) { return number_text(you->stats[STAT_DEX]); }
int   get_attribute_dex(void)      { return you->stats[STAT_DEX]; }
char *get_attribute_sta_text(void) { return number_text(you->stats[STAT_STA]); }
int   get_attribute_sta(void)      { return you->stats[STAT_STA]; }
char *get_attribute_cha_text(void) { return number_text(you->stats[STAT_CHA]); }
int   get_attribute_cha(void)      { return you->stats[STAT_CHA]; }
char *get_attribute_man_text(void) { return number_text(you->stats[STAT_MAN]); }
int   get_attribute_man(void)      { return you->stats[STAT_MAN]; }
char *get_attribute_app_text(void) { return number_text(you->stats[STAT_APP]); }
int   get_attribute_app(void)      { return you->stats[STAT_APP]; }
char *get_attribute_per_text(void) { return number_text(you->stats[STAT_PER]); }
int   get_attribute_per(void)      { return you->stats[STAT_PER]; }
char *get_attribute_int_text(void) { return number_text(you->stats[STAT_INT]); }
int   get_attribute_int(void)      { return you->stats[STAT_INT]; }
char *get_attribute_wit_text(void) { return number_text(you->stats[STAT_WIT]); }
int   get_attribute_wit(void)      { return you->stats[STAT_WIT]; }

int get_disc_animalism( void ) { return get_disc( you, DISC_ANIMALISM ); }
char *get_disc_animalism_text( void ) 
	{ return number_text(get_disc_animalism()); }
int get_disc_auspex( void ) { return get_disc( you, DISC_AUSPEX ); }
char *get_disc_auspex_text( void ) 
	{ return number_text(get_disc_auspex()); }
int get_disc_celerity( void ) { return get_disc( you, DISC_CELERITY ); }
char *get_disc_celerity_text( void ) 
	{ return number_text(get_disc_celerity()); }
int get_disc_chimerstry( void ) { return get_disc( you, DISC_CHIMERSTRY ); }
char *get_disc_chimerstry_text( void ) 
	{ return number_text(get_disc_chimerstry()); }
int get_disc_daimoinon( void ) { return get_disc( you, DISC_DAIMOINON ); }
char *get_disc_daimoinon_text( void ) 
	{ return number_text(get_disc_daimoinon()); }
int get_disc_dominate( void ) { return get_disc( you, DISC_DOMINATE ); }
char *get_disc_dominate_text( void ) 
	{ return number_text(get_disc_dominate()); }
int get_disc_fortitude( void ) { return get_disc( you, DISC_FORTITUDE ); }
char *get_disc_fortitude_text( void ) 
	{ return number_text(get_disc_fortitude()); }
int get_disc_melpominee( void ) { return get_disc( you, DISC_MELPOMINEE ); }
char *get_disc_melpominee_text( void ) 
	{ return number_text(get_disc_melpominee()); }
int get_disc_mytherceria( void ) { return get_disc( you, DISC_MYTHERCERIA ); }
char *get_disc_mytherceria_text( void ) 
	{ return number_text(get_disc_mytherceria()); }
int get_disc_necromancy( void ) { return get_disc( you, DISC_NECROMANCY ); }
char *get_disc_necromancy_text( void ) 
	{ return number_text(get_disc_necromancy()); }
int get_disc_nihilistics( void ) { return get_disc( you, DISC_NIHILISTICS ); }
char *get_disc_nihilistics_text( void ) 
	{ return number_text(get_disc_nihilistics()); }
int get_disc_obeah( void ) { return get_disc( you, DISC_OBEAH ); }
char *get_disc_obeah_text( void ) 
	{ return number_text(get_disc_obeah()); }
int get_disc_obfuscate( void ) { return get_disc( you, DISC_OBFUSCATE ); }
char *get_disc_obfuscate_text( void ) 
	{ return number_text(get_disc_obfuscate()); }
int get_disc_obtenebration( void ) { return get_disc( you, DISC_OBTENEBRATION);}
char *get_disc_obtenebration_text( void ) 
	{ return number_text(get_disc_obtenebration()); }
int get_disc_potence( void ) { return get_disc( you, DISC_POTENCE ); }
char *get_disc_potence_text( void ) 
	{ return number_text(get_disc_potence()); }
int get_disc_presence( void ) { return get_disc( you, DISC_PRESENCE ); }
char *get_disc_presence_text( void ) 
	{ return number_text(get_disc_presence()); }
int get_disc_protean( void ) { return get_disc( you, DISC_PROTEAN ); }
char *get_disc_protean_text( void ) 
	{ return number_text(get_disc_protean()); }
int get_disc_quietus( void ) { return get_disc( you, DISC_QUIETUS ); }
char *get_disc_quietus_text( void ) 
	{ return number_text(get_disc_quietus()); }
int get_disc_nex( void ) { return get_disc( you, DISC_NEX ); }
char *get_disc_nex_text( void ) 
	{ return number_text(get_disc_nex()); }
int get_disc_serpentis( void ) { return get_disc( you, DISC_SERPENTIS ); }
char *get_disc_serpentis_text( void ) 
	{ return number_text(get_disc_serpentis()); }
int get_disc_spiritus( void ) { return get_disc( you, DISC_SPIRITUS ); }
char *get_disc_spiritus_text( void ) 
	{ return number_text(get_disc_spiritus()); }
int get_disc_temporis( void ) { return get_disc( you, DISC_TEMPORIS ); }
char *get_disc_temporis_text( void ) 
	{ return number_text(get_disc_temporis()); }
int get_disc_thanatosis( void ) { return get_disc( you, DISC_THANATOSIS ); }
char *get_disc_thanatosis_text( void ) 
	{ return number_text(get_disc_thanatosis()); }
int get_disc_thaumaturgy( void ) { return get_disc( you, DISC_THAUMATURGY ); }
char *get_disc_thaumaturgy_text( void ) 
	{ return number_text(get_disc_thaumaturgy()); }
int get_disc_vicissitude( void ) { return get_disc( you, DISC_VICISSITUDE ); }
char *get_disc_vicissitude_text( void ) 
	{ return number_text(get_disc_vicissitude()); }

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

#define STRING_FUNCTION(f) VARIABLE_FUNCTION_STRING, (char *) (f)
#define STRING_CONSTANT(f) VARIABLE_CONSTANT_STRING, (f)
#define NUMBER_FUNCTION(f) VARIABLE_FUNCTION_NUMBER, (void *) (f)
#define NUMBER_CONSTANT(f) VARIABLE_CONSTANT_NUMBER, (void *) (f)
#define END_OF_LIST        VARIABLE_CONSTANT_NUMBER, NULL

/*********************************************************/
/*                 VARIABLE TABLE                        */
/*********************************************************/

const var_type var_table[] =
{
   /* Pure string functions */
   { "name",		STRING_FUNCTION(get_name) },
   { "first.name",	STRING_FUNCTION(get_firstname) },
   { "surname.name",	STRING_FUNCTION(get_surname) },
   { "full.name",	STRING_FUNCTION(get_fullname) },
   { "he/she",		STRING_FUNCTION(get_he_she) },
   { "him/her",		STRING_FUNCTION(get_him_her) },
   { "his/her",		STRING_FUNCTION(get_his_her) },

   /* Pure number functions */
   { "class",		NUMBER_FUNCTION(get_class) },
   { "dot.mode",	NUMBER_FUNCTION(get_dot_mode) },
   { "mortal",		NUMBER_CONSTANT(CLASS_NONE) },
   { "vampire",		NUMBER_CONSTANT(CLASS_VAMPIRE) },
   { "werewolf",	NUMBER_CONSTANT(CLASS_WEREWOLF) },
   { "mage",		NUMBER_CONSTANT(CLASS_MAGE) },
   { "demon",		NUMBER_CONSTANT(CLASS_DEMON) },
   { "true",		NUMBER_CONSTANT(1) },
   { "false",		NUMBER_CONSTANT(0) },
   { "caitiff",		NUMBER_CONSTANT(CLAN_CAITIFF) },
   { "lasombra",	NUMBER_CONSTANT(CLAN_LASOMBRA) },
   { "tzimisce",	NUMBER_CONSTANT(CLAN_TZIMISCE) },
   { "gangrel",		NUMBER_CONSTANT(CLAN_GANGREL) },
   { "ventrue",		NUMBER_CONSTANT(CLAN_VENTRUE) },
   { "tremere",		NUMBER_CONSTANT(CLAN_TREMERE) },
   { "assamite",	NUMBER_CONSTANT(CLAN_ASSAMITE) },
   { "malkavian",	NUMBER_CONSTANT(CLAN_MALKAVIAN) },
   { "cappadocian",	NUMBER_CONSTANT(CLAN_CAPPADOCIAN) },
   { "toreador",	NUMBER_CONSTANT(CLAN_TOREADOR) },
   { "nosferatu",	NUMBER_CONSTANT(CLAN_NOSFERATU) },
   { "unbound",		NUMBER_CONSTANT(CLAN_UNBOUND) },
   { "none",		NUMBER_CONSTANT(SECT_NONE) },
   { "canaille",	NUMBER_CONSTANT(SECT_CANAILLE) },
   { "caine",		NUMBER_CONSTANT(SECT_CAINE) },
   { "irad",		NUMBER_CONSTANT(SECT_IRAD) },
   { "enoch",		NUMBER_CONSTANT(SECT_ENOCH) },
   { "zillah",		NUMBER_CONSTANT(SECT_ZILLAH) },

   /* String AND number functions */
   { "generation",	STRING_FUNCTION(get_generation_text) },
   { "generation",	NUMBER_FUNCTION(get_generation) },
   { "max.disc",	STRING_FUNCTION(get_max_disc_text) },
   { "max.disc",	NUMBER_FUNCTION(get_max_disc) },
   { "clan",		STRING_FUNCTION(get_clan_text) },
   { "clan",		NUMBER_FUNCTION(get_clan) },
   { "sect",		STRING_FUNCTION(get_sect_text) },
   { "sect",		NUMBER_FUNCTION(get_sect) },
   { "first.clan.disc",	STRING_FUNCTION(get_clandisc1_text) },
   { "first.clan.disc",	NUMBER_FUNCTION(get_clandisc1) },
   { "second.clan.disc",STRING_FUNCTION(get_clandisc2_text) },
   { "second.clan.disc",NUMBER_FUNCTION(get_clandisc2) },
   { "third.clan.disc",	STRING_FUNCTION(get_clandisc3_text) },
   { "third.clan.disc",	NUMBER_FUNCTION(get_clandisc3) },
   { "conscience",	STRING_FUNCTION(get_conscience_text) },
   { "conscience",	NUMBER_FUNCTION(get_conscience) },
   { "self-control",	STRING_FUNCTION(get_selfcontrol_text) },
   { "self-control",	NUMBER_FUNCTION(get_selfcontrol) },
   { "courage",		STRING_FUNCTION(get_courage_text) },
   { "courage",		NUMBER_FUNCTION(get_courage) },
   { "strength",	STRING_FUNCTION(get_attribute_str_text) },
   { "strength",	NUMBER_FUNCTION(get_attribute_str) },
   { "dexterity",	STRING_FUNCTION(get_attribute_dex_text) },
   { "dexterity",	NUMBER_FUNCTION(get_attribute_dex) },
   { "stamina",		STRING_FUNCTION(get_attribute_sta_text) },
   { "stamina",		NUMBER_FUNCTION(get_attribute_sta) },
   { "charisma",	STRING_FUNCTION(get_attribute_cha_text) },
   { "charisma",	NUMBER_FUNCTION(get_attribute_cha) },
   { "manipulation",	STRING_FUNCTION(get_attribute_man_text) },
   { "manipulation",	NUMBER_FUNCTION(get_attribute_man) },
   { "appearance",	STRING_FUNCTION(get_attribute_app_text) },
   { "appearance",	NUMBER_FUNCTION(get_attribute_app) },
   { "perception",	STRING_FUNCTION(get_attribute_per_text) },
   { "perception",	NUMBER_FUNCTION(get_attribute_per) },
   { "intelligence",	STRING_FUNCTION(get_attribute_int_text) },
   { "intelligence",	NUMBER_FUNCTION(get_attribute_int) },
   { "wits",		STRING_FUNCTION(get_attribute_wit_text) },
   { "wits",		NUMBER_FUNCTION(get_attribute_wit) },
   { "animalism",	STRING_FUNCTION(get_disc_animalism_text) },
   { "animalism",	NUMBER_FUNCTION(get_disc_animalism) },
   { "auspex",		STRING_FUNCTION(get_disc_auspex_text) },
   { "auspex",		NUMBER_FUNCTION(get_disc_auspex) },
   { "celerity",	STRING_FUNCTION(get_disc_celerity_text) },
   { "celerity",	NUMBER_FUNCTION(get_disc_celerity) },
   { "chimerstry",	STRING_FUNCTION(get_disc_chimerstry_text) },
   { "chimerstry",	NUMBER_FUNCTION(get_disc_chimerstry) },
   { "daimoinon",	STRING_FUNCTION(get_disc_daimoinon_text) },
   { "daimoinon",	NUMBER_FUNCTION(get_disc_daimoinon) },
   { "dominate",	STRING_FUNCTION(get_disc_dominate_text) },
   { "dominate",	NUMBER_FUNCTION(get_disc_dominate) },
   { "fortitude",	STRING_FUNCTION(get_disc_fortitude_text) },
   { "fortitude",	NUMBER_FUNCTION(get_disc_fortitude) },
   { "melpominee",	STRING_FUNCTION(get_disc_melpominee_text) },
   { "melpominee",	NUMBER_FUNCTION(get_disc_melpominee) },
   { "mytherceria",	STRING_FUNCTION(get_disc_mytherceria_text) },
   { "mytherceria",	NUMBER_FUNCTION(get_disc_mytherceria) },
   { "necromancy",	STRING_FUNCTION(get_disc_necromancy_text) },
   { "necromancy",	NUMBER_FUNCTION(get_disc_necromancy) },
   { "nihilistics",	STRING_FUNCTION(get_disc_nihilistics_text) },
   { "nihilistics",	NUMBER_FUNCTION(get_disc_nihilistics) },
   { "obeah",		STRING_FUNCTION(get_disc_obeah_text) },
   { "obeah",		NUMBER_FUNCTION(get_disc_obeah) },
   { "obfuscate",	STRING_FUNCTION(get_disc_obfuscate_text) },
   { "obfuscate",	NUMBER_FUNCTION(get_disc_obfuscate) },
   { "obtenebration",	STRING_FUNCTION(get_disc_obtenebration_text) },
   { "obtenebration",	NUMBER_FUNCTION(get_disc_obtenebration) },
   { "potence",		STRING_FUNCTION(get_disc_potence_text) },
   { "potence",		NUMBER_FUNCTION(get_disc_potence) },
   { "presence",	STRING_FUNCTION(get_disc_presence_text) },
   { "presence",	NUMBER_FUNCTION(get_disc_presence) },
   { "protean",		STRING_FUNCTION(get_disc_protean_text) },
   { "protean",		NUMBER_FUNCTION(get_disc_protean) },
   { "quietus",		STRING_FUNCTION(get_disc_quietus_text) },
   { "quietus",		NUMBER_FUNCTION(get_disc_quietus) },
   { "nex",		STRING_FUNCTION(get_disc_nex_text) },
   { "nex",		NUMBER_FUNCTION(get_disc_nex) },
   { "serpentis",	STRING_FUNCTION(get_disc_serpentis_text) },
   { "serpentis",	NUMBER_FUNCTION(get_disc_serpentis) },
   { "spiritus",	STRING_FUNCTION(get_disc_spiritus_text) },
   { "spiritus",	NUMBER_FUNCTION(get_disc_spiritus) },
   { "temporis",	STRING_FUNCTION(get_disc_temporis_text) },
   { "temporis",	NUMBER_FUNCTION(get_disc_temporis) },
   { "thanatosis",	STRING_FUNCTION(get_disc_thanatosis_text) },
   { "thanatosis",	NUMBER_FUNCTION(get_disc_thanatosis) },
   { "thaumaturgy",	STRING_FUNCTION(get_disc_thaumaturgy_text) },
   { "thaumaturgy",	NUMBER_FUNCTION(get_disc_thaumaturgy) },
   { "vicissitude",	STRING_FUNCTION(get_disc_vicissitude_text) },
   { "vicissitude",	NUMBER_FUNCTION(get_disc_vicissitude) },
   { "get.weapon",	STRING_FUNCTION(get_wpn_text) },
   { "get.weapon",	NUMBER_FUNCTION(get_wpn) },
   { "weapon.diff",	STRING_FUNCTION(wpn_diff_text) },
   { "weapon.diff",	NUMBER_FUNCTION(wpn_diff) },
   { "weapon.damage",	STRING_FUNCTION(wpn_dam_text) },
   { "weapon.damage",	NUMBER_FUNCTION(wpn_dam) },
   { "weapon.where",	STRING_FUNCTION(wpn_where_text) },
   { "weapon.where",	NUMBER_FUNCTION(wpn_where) },

   { NULL,  END_OF_LIST } /* Always leave this at end */
};

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

static jmp_buf jBuf;
static int s_iIndex;
static int s_iLevel;

int io_calculate( CHAR_DATA *ch, char *calculation )
{
   int iResult;

   s_iIndex = -1;
   s_iLevel = 0;

   if ( setjmp( jBuf ) )
   {
      return 0; /* Unable to perform calculation */
   }

   if ( calculation[0] == '\0' )
   {
      return 0; /* Unable to perform calculation */
   }

   iResult = main_calc( ch, calculation );

   if ( s_iLevel != 1 )
   {
      char_printf("\nNon-matching brackets.\n", ch );
      return 0;
   }

   s_iIndex = -1;
   s_iLevel = 0;

   return ( iResult );
}

static int main_calc( CHAR_DATA *ch, char calc_string[] )
{
   STATE_TYPE eState     = STATE_NONE;
   int        iLeft      = 0;
   int        iRight     = 0;
   bool       bLeftNeg   = FALSE;
   bool       bRightNeg  = FALSE;
   char       chOperator = 0;
   bool       bGLEqual   = FALSE;

   s_iLevel++;

   while ( calc_string[++s_iIndex] != '\0' )
   {
      switch ( calc_string[s_iIndex] )
      {
         case '0': case '1': case '2': case '3': case '4': 
         case '5': case '6': case '7': case '8': case '9': 
            switch ( eState )
            {
               case STATE_LEFT_SIGN:
                  bLeftNeg = TRUE;
               case STATE_NONE:
               case STATE_LEFT:
               case STATE_DONE_LEFT: /* TBD - check this */
                  eState = STATE_LEFT;
                  if ( bLeftNeg == TRUE ) iLeft = 0 - iLeft; /* Make positive */
                  iLeft *= 10;
                  iLeft += (int) (calc_string[s_iIndex] - '0');
                  if ( bLeftNeg == TRUE ) iLeft = 0 - iLeft; /* Make negative */
                  break;
               case STATE_RIGHT_SIGN:
                  bRightNeg = TRUE;
               case STATE_OPERATOR:
               case STATE_RIGHT:
                  eState = STATE_RIGHT;
                  if ( bRightNeg == TRUE ) iRight = 0 - iRight; /* Make positive */
                  iRight *= 10;
                  iRight += (int) (calc_string[s_iIndex] - '0');
                  if ( bRightNeg == TRUE ) iRight = 0 - iRight; /* Make negative */
                  break;
            }
            break;

         case '+': case '-': case '*': case '/':
            if ( eState == STATE_RIGHT )
            {
               switch ( chOperator )
               {
                  case '+':
                     iLeft = iLeft + iRight;
                     break;
                  case '-':
                     iLeft = iLeft - iRight;
                     break;
                  case '*':
                     iLeft = iLeft * iRight;
                     break;
                  case '/':
                     if ( iRight == 0 )
                     {
                        char_printf("\nDivision by zero.\n", ch );
                        longjmp( jBuf, 1 );
                     }
                     iLeft = iLeft / iRight;
                     break;
                  default:
                     char_printf("\nInvalid position of closed bracket.\n", ch );
                     longjmp( jBuf, 1 );
               }
               bRightNeg = FALSE;
               iRight = 0;
               eState = STATE_OPERATOR;
            }
            else if ( eState == STATE_NONE && calc_string[s_iIndex] == '-' )
            {
               bLeftNeg = TRUE;
               eState = STATE_LEFT_SIGN;
            }
            else if ( eState == STATE_OPERATOR && calc_string[s_iIndex] == '-' )
            {
               bRightNeg = TRUE;
               eState = STATE_RIGHT_SIGN;
            }
            else if ( eState > STATE_OPERATOR )
            {
               char_printf("\nInvalid position of operator.\n", ch );
               longjmp( jBuf, 1 );
            }
            else
            {
               eState = STATE_OPERATOR;
               chOperator = calc_string[s_iIndex];
            }
            break;

         case '=': case '!':
            if ( calc_string[s_iIndex+1] != '=' )
            {
               char_printf("\nInvalid operator '%c'.\n", ch, calc_string[s_iIndex] );
               longjmp( jBuf, 1 );
            }
            ++s_iIndex;
            if ( eState == STATE_RIGHT )
            {
               switch ( chOperator )
               {
                  case '=':
                     iLeft = (iLeft == iRight);
                     break;
                  case '!':
                     iLeft = (iLeft != iRight);
                     break;
                  default:
                     char_printf("\nInvalid position of closed bracket.\n", ch );
                     longjmp( jBuf, 1 );
               }
               bRightNeg = FALSE;
               iRight = 0;
            }
            else if ( eState > STATE_OPERATOR )
            {
               char_printf("\nInvalid position of operator.\n", ch );
               longjmp( jBuf, 1 );
            }
            else
            {
               eState = STATE_OPERATOR;
               chOperator = calc_string[s_iIndex-1];
            }
            break;

         case '<': case '>':
            if ( calc_string[s_iIndex+1] == '=' )
            {
               ++s_iIndex;
               bGLEqual = TRUE;
            }
            if ( eState == STATE_RIGHT )
            {
               switch ( chOperator )
               {
                  case '[':
                     iLeft = (iLeft <= iRight);
                     break;
                  case '<':
                     iLeft = (iLeft < iRight);
                     break;
                  case ']':
                     iLeft = (iLeft >= iRight);
                     break;
                  case '>':
                     iLeft = (iLeft > iRight);
                     break;
                  default:
                     char_printf("\nInvalid position of closed bracket.\n", ch );
                     longjmp( jBuf, 1 );
               }
               bRightNeg = FALSE;
               iRight = 0;
            }
            else if ( eState > STATE_OPERATOR )
            {
               char_printf("\nInvalid position of operator.\n", ch );
               longjmp( jBuf, 1 );
            }
            else
            {
               eState = STATE_OPERATOR;
               if ( bGLEqual )
               {
                  if ( calc_string[s_iIndex-1] == '<' )
                  {
                     chOperator = '[';
                  }
                  else /* calc_string[s_iIndex-1] == '>' */
                  {
                     chOperator = ']';
                  }
               }
               else
               {
                  chOperator = calc_string[s_iIndex];
               }
            }
            bGLEqual = FALSE;
            break;

         case '&':
            if ( calc_string[s_iIndex+1] != '&' )
            {
               char_printf("\nInvalid operator '%c'.\n", ch, calc_string[s_iIndex]);
               longjmp( jBuf, 1 );
            }
            ++s_iIndex;
            if ( eState == STATE_RIGHT )
            {
               switch ( chOperator )
               {
                  case '&':
                     iLeft = (iLeft && iRight);
                     break;
                  default:
                     char_printf("\nInvalid position of closed bracket.\n",ch );
                     longjmp( jBuf, 1 );
               }
               bRightNeg = FALSE;
               iRight = 0;
            }
            else if ( eState > STATE_OPERATOR )
            {
               char_printf("\nInvalid position of operator.\n",ch );
               longjmp( jBuf, 1 );
            }
            else
            {
               eState = STATE_OPERATOR;
               chOperator = calc_string[s_iIndex-1];
            }
            break;

         case '|':
            if ( calc_string[s_iIndex+1] != '|' )
            {
               char_printf("\nInvalid operator '%c'.\n", ch, calc_string[s_iIndex] );
               longjmp( jBuf, 1 );
            }
            ++s_iIndex;
            if ( eState == STATE_RIGHT )
            {
               switch ( chOperator )
               {
                  case '|':
                     iLeft = (iLeft || iRight);
                     break;
                  default:
                     char_printf("\nInvalid position of closed bracket.\n",ch );
                     longjmp( jBuf, 1 );
               }
               bRightNeg = FALSE;
               iRight = 0;
            }
            else if ( eState > STATE_OPERATOR )
            {
               char_printf("\nInvalid position of operator.\n",ch );
               longjmp( jBuf, 1 );
            }
            else
            {
               eState = STATE_OPERATOR;
               chOperator = calc_string[s_iIndex-1];
            }
            break;

         case ' ':
            break;

         case '(':
            if ( eState == STATE_NONE )
            {
               iLeft = main_calc( ch, calc_string );
               eState = STATE_DONE_LEFT;
            }
            else if ( eState == STATE_OPERATOR )
            {
               iRight = main_calc( ch, calc_string );
               eState = STATE_RIGHT;
            }
            else
            {
               char_printf("\nInvalid position of open bracket.\n",ch );
               longjmp( jBuf, 1 );
            }
            break;

         case ')':
            s_iLevel--;
            if ( eState == STATE_DONE_LEFT || eState == STATE_LEFT )
            {
               return ( iLeft );
            }
            if ( eState != STATE_RIGHT )
            {
               char_printf("\nInvalid position of closed bracket.\n",ch );
               longjmp( jBuf, 1 );
            }
            switch ( chOperator )
            {
               case '+': return ( iLeft + iRight );
               case '-': return ( iLeft - iRight );
               case '*': return ( iLeft * iRight );
               case '/': 
                  if ( iRight == 0 )
                  {
                     char_printf("\nDivision by zero.\n", ch );
                     longjmp( jBuf, 1 );
                  }
                  return ( iLeft / iRight );
               case '=': return ( iLeft == iRight );
               case '!': return ( iLeft != iRight );
               case '<': return ( iLeft < iRight );
               case '>': return ( iLeft > iRight );
               case '[': return ( iLeft <= iRight );
               case ']': return ( iLeft >= iRight );
               case '&': return ( iLeft && iRight );
               case '|': return ( iLeft || iRight );
            }
            char_printf("\nInvalid position of closed bracket.\n",ch );
            longjmp( jBuf, 1 );

         case '\0':
            if ( s_iLevel > 1 )
            {
               char_printf("\nInvalid character '%d'.\n", ch, calc_string[s_iIndex] );
               longjmp( jBuf, 1 );
            }
            char_printf("\nDone.\n",ch );
            longjmp( jBuf, 1 );


         default: /* zzz */
            char_printf("\nInvalid character '%c'.\n", ch, calc_string[s_iIndex] );
            longjmp( jBuf, 1 );
      }
   };

   if ( s_iLevel != 1 )
   {
      char_printf("\nNon-matching brackets.\n", ch );
      longjmp( jBuf, 1 );
   }

   if ( eState != STATE_RIGHT )
   {
      if ( eState == STATE_DONE_LEFT || eState == STATE_LEFT )
      {
         return ( iLeft );
      }
      char_printf("\nInvalid calc_string.\n", ch );
      longjmp( jBuf, 1 );
   }
   switch ( chOperator )
   {
      case '+': return ( iLeft + iRight );
      case '-': return ( iLeft - iRight );
      case '*': return ( iLeft * iRight );
      case '/': 
         if ( iRight == 0 )
         {
            char_printf("\nDivision by zero.\n", ch );
            longjmp( jBuf, 1 );
         }
         return ( iLeft / iRight );
      case '=': return ( iLeft == iRight );
      case '!': return ( iLeft != iRight );
      case '<': return ( iLeft < iRight );
      case '>': return ( iLeft > iRight );
      case '[': return ( iLeft <= iRight );
      case ']': return ( iLeft >= iRight );
      case '&': return ( iLeft && iRight );
      case '|': return ( iLeft || iRight );
   }
   char_printf("\nInvalid calculation.\n", ch );

   return 0;
}

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

void io_parse( CHAR_DATA *ch, char *in_ptr, char *out_ptr )
{
   char var_buf[256];
   int  var_count = 0;
   bool var_name = FALSE;
   char calc_buf[256];
   int  calc_count = 0;
   bool calc_name = FALSE;
   int  nest_text = 0;
   int  valid_nest = 0;

   you = ch; /* Initialise the 'you' variable used in function table */
   setup_variables(ch);

   while ( *in_ptr )
   {
      switch ( *in_ptr )
      {
         default:
            if ( valid_nest != nest_text )
            {
               in_ptr++;
            }
            else if ( var_name )
            {
               var_buf[var_count++] = *in_ptr++;
            }
            else if ( calc_name )
            {
               calc_buf[calc_count++] = *in_ptr++;
            }
            else
            {
               *out_ptr++ = *in_ptr++;
            }
            break;
         case '{':
            if ( valid_nest != nest_text )
            {
               in_ptr++;
               break;
            }
            in_ptr++;
            var_buf[var_count=0] = '\0';
            if ( var_name ) char_printf( "Nested variable names not allowed.\n\r", ch );
            var_name = TRUE;
            break;
         case '}':
            if ( valid_nest != nest_text )
            {
               in_ptr++;
               break;
            }
            if ( !var_name ) char_printf( "Variable terminator without variable.\n\r", ch );
            var_buf[var_count] = '\0';
            in_ptr++;
            var_name = FALSE;
            if ( calc_name )
            {
               (void) number_var( &var_buf[0], &calc_buf[calc_count] ); /* Calculator variable */
               calc_count = strlen( calc_buf ); /* Nasty hack to recalculate calc_count */
            }
            else
               string_var( &var_buf[0], &out_ptr ); /* String variable */
            break;
         case '[':
            if ( valid_nest != nest_text )
            {
               in_ptr++;
               break;
            }
            in_ptr++;
            calc_buf[calc_count=0] = '\0';
            if ( calc_name ) char_printf( "Nested calculations not allowed.\n\r", ch );
            calc_name = TRUE;
            break;
         case ']':
            if ( valid_nest != nest_text )
            {
               nest_text++;
               in_ptr++;
               break;
            }
            if ( !calc_name ) char_printf( "Calculation terminator without calculation.\n\r", ch );
            calc_buf[calc_count] = '\0';
            in_ptr++;
            nest_text++;
            valid_nest += !!io_calculate( ch, &calc_buf[0] );
            calc_name = FALSE;
            break;
         case '|':
            if ( --nest_text < 0 )
            {
               char_printf( "Conditional terminator without condition.\n\r", ch );
               nest_text = 0;
            }
            if ( valid_nest > nest_text ) valid_nest = nest_text;
            in_ptr++;
            break;
      }
   }
   *out_ptr = '\0';
}

bool string_var( char *var_ptr, char **out_ptr )
{
   int i = -1;

   while ( var_table[++i].variable != NULL )
   {
      if ( !strcmp( var_ptr, var_table[i].variable ) )
      {
         char *replace_ptr;

         switch ( var_table[i].type )
         {
            case VARIABLE_FUNCTION_STRING:
               replace_ptr = ((char*(*)())(var_table[i].replace.text))();
               break;
            case VARIABLE_CONSTANT_STRING:
               replace_ptr = var_table[i].replace.text;
               break;
            default: continue; /* Cannot resolve numbers */
         }

         while ( *replace_ptr ) *(*out_ptr)++ = *replace_ptr++;

         return TRUE;
      }
   }
   return FALSE;
}

bool number_var( char *var_ptr, char *out_ptr )
{
   int i = -1;

   while ( var_table[++i].variable != NULL )
   {
      if ( !strcmp( var_ptr, var_table[i].variable ) )
      {
         int   replace_num;
         char  replace_buf[64];
         char *replace_ptr = &replace_buf[0];

         switch ( var_table[i].type )
         {
            case VARIABLE_FUNCTION_NUMBER:
               replace_num = ((int(*)())(var_table[i].replace.number))();
               break;
            case VARIABLE_CONSTANT_NUMBER:
               replace_num = var_table[i].replace.number;
               break;
            default: continue; /* Cannot resolve strings */
         }

         sprintf( replace_ptr, "%d", replace_num );

         while ( (*out_ptr++ = *replace_ptr++) );

         return TRUE;
      }
   }
   return FALSE;
}

void io_display( CHAR_DATA *ch, char text[] )
{
/*
   char buf [MAX_STRING_LENGTH] = { '\0' };
*/
   int align = 0;
   int i;

   while ( *text )
   {
      switch ( *text )
      {
         default:
            char_printf( "%c", ch, *text );
            break;
         case '[':
            char_printf( "\n",ch );
            for ( i = 0; i < align; i++ ) char_printf( "   ",ch );
            char_printf( "[",ch );
            break;
         case ']':
            char_printf( "]\n",ch );
            align++;
            for ( i = 0; i < align; i++ ) char_printf( "   ",ch );
            break;
         case '|':
            char_printf( "\n",ch );
            align--;
            for ( i = 0; i < align; i++ ) char_printf( "   ",ch );
            char_printf( "|",ch );
            break;
      }
      text++;
   }
   char_printf( "\n",ch );
}

void io_main( CHAR_DATA *ch )
{
   char in_buf  [256];
   char out_buf [256];

   strcpy( in_buf, 
"Hello there!"
"[{str}!=18]How are you?"
"[1]Are you okay?||" );

   io_display( ch, in_buf );

   io_parse( ch, in_buf, out_buf );

   char_printf( "String:[%s]\n\r", ch,out_buf );
}

static void setup_variables ( CHAR_DATA *ch )
{
   OBJ_DATA *obj,  *obj2;

   wpn = NULL;

   for ( obj = ch->carrying; obj; obj = obj->next_content )
   {
      if ( obj->item_type == ITEM_WEAPON )
      {
         wpn = obj;
         break;
      }
   }

   if ( wpn ) return;

   for ( obj = ch->carrying; obj; obj = obj->next_content )
   {
      for ( obj2 = obj->contains; obj2; obj2 = obj2->next_content )
      {
         if ( obj2->item_type == ITEM_WEAPON )
         {
            wpn = obj2;
            break;
         }
      }
   }
}

