skylib_fluffos_v3/
skylib_fluffos_v3/bin/
skylib_fluffos_v3/bin/db/
skylib_fluffos_v3/fluffos-2.9-ds2.04/
skylib_fluffos_v3/fluffos-2.9-ds2.04/ChangeLog.old/
skylib_fluffos_v3/fluffos-2.9-ds2.04/Win32/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/simuls/
skylib_fluffos_v3/fluffos-2.9-ds2.04/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/clone/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/command/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/data/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/etc/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/master/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/log/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/compiler/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/efuns/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/operators/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/u/
skylib_fluffos_v3/fluffos-2.9-ds2.04/tmp/
skylib_fluffos_v3/fluffos-2.9-ds2.04/windows/
skylib_fluffos_v3/mudlib/
skylib_fluffos_v3/mudlib/cmds/
skylib_fluffos_v3/mudlib/cmds/admin/
skylib_fluffos_v3/mudlib/cmds/guild-race/
skylib_fluffos_v3/mudlib/cmds/living/broken/
skylib_fluffos_v3/mudlib/cmds/player/group_cmds/
skylib_fluffos_v3/mudlib/cmds/playtester/
skylib_fluffos_v3/mudlib/d/admin/
skylib_fluffos_v3/mudlib/d/admin/room/
skylib_fluffos_v3/mudlib/d/admin/room/we_care/
skylib_fluffos_v3/mudlib/d/admin/save/
skylib_fluffos_v3/mudlib/d/admin/text/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/buildings/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/map/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/roads/
skylib_fluffos_v3/mudlib/d/learning/chars/
skylib_fluffos_v3/mudlib/d/learning/functions/
skylib_fluffos_v3/mudlib/d/learning/handlers/
skylib_fluffos_v3/mudlib/d/learning/help_topics/
skylib_fluffos_v3/mudlib/d/learning/help_topics/npcs/
skylib_fluffos_v3/mudlib/d/learning/help_topics/objects/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/crowd/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/situations/
skylib_fluffos_v3/mudlib/d/learning/save/
skylib_fluffos_v3/mudlib/d/learning/school/
skylib_fluffos_v3/mudlib/d/learning/school/add_sc/
skylib_fluffos_v3/mudlib/d/learning/school/characters/
skylib_fluffos_v3/mudlib/d/learning/school/general/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/basic_commands/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/edtutor/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_fluffos_v3/mudlib/d/learning/school/items/
skylib_fluffos_v3/mudlib/d/learning/school/npc_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/room_basic/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/situations/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_fluffos_v3/mudlib/d/learning/text/
skylib_fluffos_v3/mudlib/d/liaison/
skylib_fluffos_v3/mudlib/d/mudlib/
skylib_fluffos_v3/mudlib/d/mudlib/changes/
skylib_fluffos_v3/mudlib/d/playtesters/
skylib_fluffos_v3/mudlib/d/playtesters/effects/
skylib_fluffos_v3/mudlib/d/playtesters/handlers/
skylib_fluffos_v3/mudlib/d/playtesters/items/
skylib_fluffos_v3/mudlib/d/sage/
skylib_fluffos_v3/mudlib/doc/
skylib_fluffos_v3/mudlib/doc/creator/
skylib_fluffos_v3/mudlib/doc/driver/
skylib_fluffos_v3/mudlib/doc/driver/efuns/arrays/
skylib_fluffos_v3/mudlib/doc/driver/efuns/buffers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/calls/
skylib_fluffos_v3/mudlib/doc/driver/efuns/compile/
skylib_fluffos_v3/mudlib/doc/driver/efuns/filesystem/
skylib_fluffos_v3/mudlib/doc/driver/efuns/floats/
skylib_fluffos_v3/mudlib/doc/driver/efuns/functions/
skylib_fluffos_v3/mudlib/doc/driver/efuns/general/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mappings/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mixed/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mudlib/
skylib_fluffos_v3/mudlib/doc/driver/efuns/numbers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/parsing/
skylib_fluffos_v3/mudlib/doc/login/
skylib_fluffos_v3/mudlib/doc/lpc/basic_manual/
skylib_fluffos_v3/mudlib/doc/lpc/intermediate/
skylib_fluffos_v3/mudlib/doc/new/add_command/
skylib_fluffos_v3/mudlib/doc/new/events/
skylib_fluffos_v3/mudlib/doc/new/handlers/
skylib_fluffos_v3/mudlib/doc/new/living/race/
skylib_fluffos_v3/mudlib/doc/new/living/spells/
skylib_fluffos_v3/mudlib/doc/new/object/
skylib_fluffos_v3/mudlib/doc/new/player/
skylib_fluffos_v3/mudlib/doc/new/room/guild/
skylib_fluffos_v3/mudlib/doc/new/room/outside/
skylib_fluffos_v3/mudlib/doc/new/room/storeroom/
skylib_fluffos_v3/mudlib/doc/object/
skylib_fluffos_v3/mudlib/doc/playtesters/
skylib_fluffos_v3/mudlib/doc/policy/
skylib_fluffos_v3/mudlib/doc/weapons/
skylib_fluffos_v3/mudlib/global/
skylib_fluffos_v3/mudlib/global/creator/
skylib_fluffos_v3/mudlib/handlers/
skylib_fluffos_v3/mudlib/include/casino/
skylib_fluffos_v3/mudlib/include/cmds/
skylib_fluffos_v3/mudlib/include/effects/
skylib_fluffos_v3/mudlib/include/npc/
skylib_fluffos_v3/mudlib/include/room/
skylib_fluffos_v3/mudlib/include/shops/
skylib_fluffos_v3/mudlib/net/daemon/
skylib_fluffos_v3/mudlib/net/daemon/chars/
skylib_fluffos_v3/mudlib/net/inherit/
skylib_fluffos_v3/mudlib/net/obj/
skylib_fluffos_v3/mudlib/net/obj/BACKUPS/
skylib_fluffos_v3/mudlib/obj/amulets/
skylib_fluffos_v3/mudlib/obj/armours/plate/
skylib_fluffos_v3/mudlib/obj/b_day/
skylib_fluffos_v3/mudlib/obj/clothes/transport/horse/
skylib_fluffos_v3/mudlib/obj/faith/symbols/
skylib_fluffos_v3/mudlib/obj/fungi/
skylib_fluffos_v3/mudlib/obj/gatherables/
skylib_fluffos_v3/mudlib/obj/instruments/
skylib_fluffos_v3/mudlib/obj/media/
skylib_fluffos_v3/mudlib/obj/misc/player_shop/
skylib_fluffos_v3/mudlib/obj/monster/godmother/
skylib_fluffos_v3/mudlib/obj/monster/transport/
skylib_fluffos_v3/mudlib/obj/rings/
skylib_fluffos_v3/mudlib/obj/scabbards/
skylib_fluffos_v3/mudlib/obj/spells/
skylib_fluffos_v3/mudlib/obj/stationery/
skylib_fluffos_v3/mudlib/obj/stationery/envelopes/
skylib_fluffos_v3/mudlib/obj/toys/
skylib_fluffos_v3/mudlib/obj/vessels/
skylib_fluffos_v3/mudlib/obj/weapons/axes/
skylib_fluffos_v3/mudlib/obj/weapons/chains/
skylib_fluffos_v3/mudlib/obj/weapons/maces/BACKUPS/
skylib_fluffos_v3/mudlib/save/autodoc/
skylib_fluffos_v3/mudlib/save/book_handler/
skylib_fluffos_v3/mudlib/save/books/history/calarien/
skylib_fluffos_v3/mudlib/save/mail/
skylib_fluffos_v3/mudlib/save/new_soul/data/
skylib_fluffos_v3/mudlib/save/parcels/
skylib_fluffos_v3/mudlib/save/playerinfo/
skylib_fluffos_v3/mudlib/save/players/d/
skylib_fluffos_v3/mudlib/save/players/s/
skylib_fluffos_v3/mudlib/save/random_names/
skylib_fluffos_v3/mudlib/save/random_names/data/
skylib_fluffos_v3/mudlib/save/terrains/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_desert/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_grassy_field/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_mountain/
skylib_fluffos_v3/mudlib/save/todo_lists/
skylib_fluffos_v3/mudlib/secure/
skylib_fluffos_v3/mudlib/secure/cmds/admin/
skylib_fluffos_v3/mudlib/secure/cmds/lord/
skylib_fluffos_v3/mudlib/secure/config/
skylib_fluffos_v3/mudlib/secure/handlers/autodoc/
skylib_fluffos_v3/mudlib/secure/handlers/intermud/
skylib_fluffos_v3/mudlib/secure/include/global/
skylib_fluffos_v3/mudlib/secure/save/
skylib_fluffos_v3/mudlib/secure/save/handlers/
skylib_fluffos_v3/mudlib/secure/std/
skylib_fluffos_v3/mudlib/secure/std/classes/
skylib_fluffos_v3/mudlib/secure/std/modules/
skylib_fluffos_v3/mudlib/std/creator/
skylib_fluffos_v3/mudlib/std/dom/
skylib_fluffos_v3/mudlib/std/effects/
skylib_fluffos_v3/mudlib/std/effects/external/
skylib_fluffos_v3/mudlib/std/effects/fighting/
skylib_fluffos_v3/mudlib/std/effects/magic/
skylib_fluffos_v3/mudlib/std/effects/magic/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/other/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/priest/
skylib_fluffos_v3/mudlib/std/effects/room/
skylib_fluffos_v3/mudlib/std/environ/
skylib_fluffos_v3/mudlib/std/guilds/
skylib_fluffos_v3/mudlib/std/guilds/old/
skylib_fluffos_v3/mudlib/std/languages/
skylib_fluffos_v3/mudlib/std/liquids/
skylib_fluffos_v3/mudlib/std/npc/
skylib_fluffos_v3/mudlib/std/npc/goals/
skylib_fluffos_v3/mudlib/std/npc/goals/basic/
skylib_fluffos_v3/mudlib/std/npc/goals/misc/
skylib_fluffos_v3/mudlib/std/npc/plans/
skylib_fluffos_v3/mudlib/std/npc/plans/basic/
skylib_fluffos_v3/mudlib/std/npc/types/
skylib_fluffos_v3/mudlib/std/npc/types/helper/
skylib_fluffos_v3/mudlib/std/npcs/
skylib_fluffos_v3/mudlib/std/outsides/
skylib_fluffos_v3/mudlib/std/races/shadows/
skylib_fluffos_v3/mudlib/std/room/basic/BACKUPS/
skylib_fluffos_v3/mudlib/std/room/basic/topography/
skylib_fluffos_v3/mudlib/std/room/controller/
skylib_fluffos_v3/mudlib/std/room/inherit/topography/
skylib_fluffos_v3/mudlib/std/room/topography/area/
skylib_fluffos_v3/mudlib/std/room/topography/iroom/
skylib_fluffos_v3/mudlib/std/room/topography/milestone/
skylib_fluffos_v3/mudlib/std/shadows/curses/
skylib_fluffos_v3/mudlib/std/shadows/disease/
skylib_fluffos_v3/mudlib/std/shadows/fighting/
skylib_fluffos_v3/mudlib/std/shadows/healing/
skylib_fluffos_v3/mudlib/std/shadows/magic/
skylib_fluffos_v3/mudlib/std/shadows/poison/
skylib_fluffos_v3/mudlib/std/shadows/room/
skylib_fluffos_v3/mudlib/std/shops/controllers/
skylib_fluffos_v3/mudlib/std/shops/objs/
skylib_fluffos_v3/mudlib/std/shops/player_shop/
skylib_fluffos_v3/mudlib/std/socket/
skylib_fluffos_v3/mudlib/std/soul/d/
skylib_fluffos_v3/mudlib/std/soul/e/
skylib_fluffos_v3/mudlib/std/soul/i/
skylib_fluffos_v3/mudlib/std/soul/j/
skylib_fluffos_v3/mudlib/std/soul/k/
skylib_fluffos_v3/mudlib/std/soul/l/
skylib_fluffos_v3/mudlib/std/soul/n/
skylib_fluffos_v3/mudlib/std/soul/o/
skylib_fluffos_v3/mudlib/std/soul/q/
skylib_fluffos_v3/mudlib/std/soul/r/
skylib_fluffos_v3/mudlib/std/soul/u/
skylib_fluffos_v3/mudlib/std/soul/v/
skylib_fluffos_v3/mudlib/std/soul/y/
skylib_fluffos_v3/mudlib/std/soul/z/
skylib_fluffos_v3/mudlib/std/stationery/
skylib_fluffos_v3/mudlib/w/
skylib_fluffos_v3/mudlib/w/default/
skylib_fluffos_v3/mudlib/w/default/armour/
skylib_fluffos_v3/mudlib/w/default/clothes/
skylib_fluffos_v3/mudlib/w/default/item/
skylib_fluffos_v3/mudlib/w/default/npc/
skylib_fluffos_v3/mudlib/w/default/room/
skylib_fluffos_v3/mudlib/w/default/weapon/
skylib_fluffos_v3/mudlib/www/
skylib_fluffos_v3/mudlib/www/java/
skylib_fluffos_v3/mudlib/www/secure/
skylib_fluffos_v3/mudlib/www/secure/lpc/advanced/
skylib_fluffos_v3/mudlib/www/secure/lpc/intermediate/
skylib_fluffos_v3/win32/
/**
 * This is the file containing extra controls for the player run
 * craft shop.  We will assume that all player run craft shops will be
 * rooms and not npcs.
 * @author Pinkfish
 * @started Fri Apr 21 13:37:44 PDT 2000
 */

inherit "/std/shops/inherit/craft_shop_category";
inherit "/std/basic/expressions";

#include <player.h>
#include <move_failures.h>
#include <shops/craft_shop.h>
#include <clubs.h>
#define EXPRESSION_NO_CLASSES
#include <expressions.h>
#include <mail.h>

#define PLAYER_CRAFT_SHOP_CHECK_PROP "player craft shop check"

#define PLAYER_CRAFT_SHOP_SELL      1
#define PLAYER_CRAFT_SHOP_SELL_AUTO 2
#define PLAYER_CRAFT_SHOP_BUY       3
#define PLAYER_CRAFT_SHOP_APPROVE   4

#define PLAYER_CRAFT_SHOP_EXPR_DENY   1
#define PLAYER_CRAFT_SHOP_EXPR_ACCEPT 2

class expression_type {
   int type;
   class parse_node* condition;
   class parse_node* value;
}

class approval {
   mixed hairy;
   class expression_type* expressions;
   int low_cost;
   int high_cost;
   mapping items;
   int num_allowed;
   int high_cost_deny;
}

class approval_obs {
   string seller;
   string category;
   int value;
   string name;
   int* saved;
   int enter_time;
   int* checkout;
}

class shop_transaction {
   int time;
   string person;
   string objects;
   string name_cat;
   int type;
   int amount;
   mixed extra;
}

class shop_stats {
   int num_sold;
   int value_sold;
}

class seller_information {
   int max_sellable;
   int value_limit;
   int deny_value_limit;
}

private class approval_obs* _waiting_for_approval;
private class approval _automatic_approval;
private int _automatic_percentage;
private int _owner_money;
private int _current_save_num;
private int _total_outgoing;
private int _total_ingoing;
private string _shop_name;
private class shop_transaction* _transactions;
private mapping _sell_stats;
private int _stats_start;
private mapping _sellers;
private string *_black_list;
private class parse_node* _buy_expression;
private class parse_node* _use_expression;
private string _auto_load_sign_str;

private nosave string _parcel_post;
private nosave int _something_checkedout;
private nosave int _maximum_inventory;
private nosave object _sign_ob;

protected void confirm_approval(string answer, class approval_obs approve);
private mixed* parse_or(string str);
private int variable_player_level(string seller, int cost, object* ob);
private string variable_player_deity(string seller, int cost, object* ob);
private string variable_player_family(string seller, int cost, object* ob);
private string variable_object_type(string seller, int cost, object* ob);
private int variable_object_value(string seller, int cost, object* ob);
private int variable_object_base_value(string seller, int cost, object* ob);
private int variable_object_condition(string seller, int cost, object* ob);
private int variable_object_enchant(string seller, int cost, object* ob);
private int function_club_member(string club, string seller, int cost, object* ob);
private int function_object_contains_spell(string spell, string seller, int cost, object* ob);
private int function_object_percentage_liquid(string liquid, string seller, int cost, object* ob);
private int function_object_matching(string match, string seller, int cost, object* ob);
private int function_object_short(string short, int all_matching, string seller, int cost, object* ob);


/*
 * This methods must be defined in an upper level class.
 */
string query_owner();
int is_allowed(string person);
void do_save();
void event_save(object thing);
string *query_allowed();
void set_short(string short);
void add_property(string name, mixed value);

void create() {
   if (!_waiting_for_approval) {
      _waiting_for_approval = ({ });
   }
   if (!_automatic_approval) {
      _automatic_approval = new(class approval);
      //_automatic_approval->sellers = ({ });
      //_automatic_approval->categories = ([ ]);
      _automatic_approval->expressions = ({ });
      _automatic_approval->items = ([ ]);
   }
   if (!_automatic_percentage) {
      _automatic_percentage = 10;
   }
   if (!_transactions) {
      _transactions = ({ });
   }
   if (!_sell_stats) {
      _sell_stats = ([ ]);
   }
   if (!_stats_start) {
      _stats_start = time();
   }
   if (!_sellers) {
      _sellers = ([ ]);
   }
   if (!_black_list) {
      _black_list = ({ });
   }
   if (!_buy_expression) {
      _buy_expression = ({ });
   }
   if (!_use_expression) {
      _use_expression = ({ });
   }
   //
   // Set it up so that selling it the normal way gets no money.
   //
   set_cut(100);
   craft_shop_category::create();
   expressions::create();
   if (_shop_name) {
      set_short(_shop_name);
      add_property("determinate", "");
   }
   set_always_ask_price(1);

   add_allowed_variable("level", EXPRESSION_TYPE_INTEGER,
                        (: variable_player_level :) );
   add_allowed_variable("deity", EXPRESSION_TYPE_STRING,
                        (: variable_player_deity :) );
   add_allowed_variable("family", EXPRESSION_TYPE_STRING,
                        (: variable_player_family :) );
   add_allowed_variable("objectenchant", EXPRESSION_TYPE_INTEGER,
                        (: variable_object_enchant :) );
   add_allowed_variable("objectvalue", EXPRESSION_TYPE_MONEY,
                        (: variable_object_value :) );
   add_allowed_variable("objectbasevalue", EXPRESSION_TYPE_MONEY,
                        (: variable_object_base_value :) );
   add_allowed_variable("objectcondition", EXPRESSION_TYPE_INTEGER,
                        (: variable_object_condition :) );
   add_allowed_variable("objecttype", EXPRESSION_TYPE_STRING,
                        (: variable_object_type :) );
   add_allowed_function("clubmember", EXPRESSION_TYPE_BOOLEAN,
                        ({ EXPRESSION_TYPE_STRING }),
                        (: function_club_member :) );
   add_allowed_function("containsspell", EXPRESSION_TYPE_BOOLEAN,
                        ({ EXPRESSION_TYPE_STRING }),
                        (: function_object_contains_spell :) );
   add_allowed_function("percentageliquid", EXPRESSION_TYPE_INTEGER,
                        ({ EXPRESSION_TYPE_STRING }),
                        (: function_object_percentage_liquid :) );
   add_allowed_function("objectmatch", EXPRESSION_TYPE_BOOLEAN,
                        ({ EXPRESSION_TYPE_STRING }),
                        (: function_object_matching :) );
   add_allowed_function("objectshort", EXPRESSION_TYPE_BOOLEAN,
                        ({ EXPRESSION_TYPE_STRING,
                           EXPRESSION_TYPE_BOOLEAN }),
                        (: function_object_short :) );
} /* create() */

/**
 * This method sets the maximum inventory size for the shop.
 * @param size the maximum inventory size
 */
void set_maximum_inventory_size(int size) {
   _maximum_inventory = size;
} /* set_maximum_inventory_size() */

/**
 * This method returns the maximum inventory size for the shop.
 * @return the maximum inventory size
 */
int query_maximum_inventory_size() {
   return _maximum_inventory;
} /* query_maximum_inventory_size() */

/**
 * This method sets the parcel post to use for rejecting items.
 * @param parcel the parcel post office
 */
void set_parcel_post(string parcel) {
   _parcel_post = parcel;
} /* set_parcel_post() */

/**
 * This method tells us the current parcel post used for rejecting items.
 * @return the current parcel post
 */
string query_parcel_post() {
   return _parcel_post;
} /* query_parcel_post() */

/**
 * This method sets the name of the shop.
 * @param name the name of the shop
 */
void set_shop_name(string name) {
   _shop_name = name;
   add_property("determinate", "");
   set_short(_shop_name);
} /* set_shop_name() */

/**
 * This method returns the name of the shop.
 * @return the name of the shop
 */
string query_shop_name() {
   return _shop_name;
} /* query_shop_name() */

/**
 * This method returns the current approval list for the shop.
 */
class approval_obs* query_approval_list() {
   return _waiting_for_approval;
} /* query_approval_list() */

/**
 * This method returns the number of items currently in the shop by
 * the specified person.  This includes items waiting for approval.
 */
int query_number_of_items_listed(string name) {
   int num;
   class approval_obs bing;

   name = lower_case(name);
   num = sizeof(query_controller()->query_owner_sellables(name));
   foreach (bing in _waiting_for_approval) {
      if (lower_case(bing->seller) == name) {
         num += sizeof(bing->saved);
      }
   }
   return num;
} /* query_number_of_items_listed() */

/** 
 * This method checks to see if the specified item is approved.
 * @param seller the name of the seller
 * @param value the amount it is sold for
 * @param name the name of the item being solde
 * @param sellables the sold items
 * @param category the category of the sold item
 * @return 1 if is approved, 0 if not, -1 if denied
 */
int is_item_approved(string seller, int value, string name, object* sellables,
                     string category) {
   string short;
   object ob;
   int item_approval;
   class expression_type bing;
   class parse_node frog;
   int cost;

   if (is_allowed(seller)) {
      return 1;
   }

   //
   // This is the value we don't automatically accept at.
   //
   seller = lower_case(seller);
   if (_sellers[seller] &&
       _sellers[seller]->deny_value_limit) {
      if (_sellers[seller]->deny_value_limit < value) {
         return -1;
      }
   } else if (_automatic_approval->high_cost_deny &&
              _automatic_approval->high_cost_deny <= value) {
      return -1;
   }

   //
   // Check through the automatic approval stuff to see if we should
   // approve it.
   //
   foreach (ob in sellables) {
      short = ob->query_short();
      if (_automatic_approval->items[short]) {
         if (pointerp(_automatic_approval->items[short])) {
            frog = evaluate_expression(_automatic_approval->items[short],
                                     seller,
                                     value,
                                     ({ ob }));
            cost = frog->value;
            if (cost < value) {
               item_approval = 0;
               break;
            }
            item_approval++;
         } else if (_automatic_approval->items[short] < value) {
            item_approval = 0;
            break;
         } else {
            item_approval++;
         }
      }
   }

   if (item_approval) {
      return 1;
   }

   //
   // Then check any setup expressions.
   //
   if (!pointerp(_automatic_approval->expressions)) {
      _automatic_approval->expressions = ({ });
   }
   foreach (bing in _automatic_approval->expressions) {
      if (evaluate_expression(bing->condition,
                              seller,
                              value,
                              sellables)->value) {
         if (bing->type == PLAYER_CRAFT_SHOP_EXPR_ACCEPT) {
            frog = evaluate_expression(bing->value,
                                 seller,
                                 value,
                                 sellables);
            cost = frog->value;
            if (cost > value) {
               return 1;
            }
         } else {
            return -1;
         }
      }
   }

   if (_sellers[seller] &&
       _sellers[seller]->value_limit) {
      if (_sellers[seller]->value_limit < value) {
         return 0;
      } else {
         return 1;
      }
   } else if (_automatic_approval->high_cost &&
              _automatic_approval->high_cost < value) {
      return 0;
   }

   if (_automatic_approval->low_cost &&
       _automatic_approval->low_cost >= value) {
      return 1;
   }

   return 0;
} /* is_item_approved() */

/**
 * This method checks to see if the shop is a closed shop and then checks
 * to see if the person is allowed.
 * @param person the person to check to see if they are allowed
 * @return 1 if they are allowed, 0 if not
 */
int is_allowed_to_use_shop(string person) {
   class parse_node frog;

   if (is_allowed(person)) {
      return 1;
   }

   frog = evaluate_expression(_use_expression, person, 0, ({ }));
   return frog->value;
} /* is_allowed_to_use_shop() */

/**
 * @ignore yes
 */
int check_open(object player) {
   if (!::check_open(player)) {
      return 0;
   }

   if (member_array(player->query_name(), _black_list) != -1) {
      add_failed_mess("You are not allowed to use this shop.\n");
      return 0;
   }

   if (!is_allowed_to_use_shop(player->query_name())) {
      add_failed_mess("You are not allowed to use this shop.\n");
      return 0;
   }

   return 1;
} /* check_open() */

/**
 * @ignore yes
 */
void set_controller(string name) {
   ::set_controller(name);
   query_controller()->set_dont_use_name(1);
} /* set_controller() */

/**
 * @ignore yes
 */
int is_allowed_to_sell(object *obs, string name, string *sellable_names) {
   int num;
   int max;

   if (_automatic_approval->num_allowed ||
       _sellers[name]) {
      num = query_number_of_items_listed(name) + sizeof(obs);
      if (_sellers[name] && _sellers[name]->max_sellable) {
         max = _sellers[name]->max_sellable;
      } else {
         max = _automatic_approval->num_allowed;
      }

      if (num > max) {
         add_failed_mess("You cannot have more than " + max + " items "
                         "listed at " + the_short() + ", with this sale you "
                         "would have " +
                         num + ".\n");
         return 0;
      }
   }

   if (member_array(lower_case(name), _black_list) != -1) {
      add_failed_mess("You are not allowed to sell things here.\n");
      return 0;
   }

   //
   // Otherwise see if we are over the shops total maximum size.
   //
   num = sizeof(query_controller()->query_sell_list_obs());
   if (num + sizeof(obs) > query_maximum_inventory_size()) {
      add_failed_mess("The shop is full, it can only hold " +
                      query_maximum_inventory_size() + " and it currently "
                      "holds " + num + ".\n");
      return 0;
   }
   return 1;
} /* is_allowed_to_sell() */

/**
 * This method finds out the maximum value that this item is allowed
 * to be sold for based on the current settings of the shop.
 * @param person the person doing the selling
 * @param items the item being sold
 * @return ({ queue limit, deny limit })
 */
int* query_maximum_sale_value_both(string person, object item) {
   int max_deny;
   int max_accept;
   string short;
   class expression_type stuff;

   if (_sellers[person]) {
      if (_sellers[person]->value_limit) {
         max_accept = _sellers[person]->value_limit;
      }
      if (_sellers[person]->deny_value_limit) {
         max_deny = _sellers[person]->deny_value_limit;
      }
   }
   if (!max_accept) {
      max_accept = _automatic_approval->high_cost;
   }
   if (!max_deny) {
      max_deny = _automatic_approval->high_cost_deny;
   }

   short = item->query_short();
   if (_automatic_approval->items[short]) {
      if (pointerp(_automatic_approval->items[short])) {
         max_accept = evaluate_expression(_automatic_approval->items[short],
                                        person, 0, ({ }))->value;
      } else if (_automatic_approval->items[short] < max_deny) {
         max_accept = _automatic_approval->items[short];
      }
   }

   foreach (stuff in _automatic_approval->expressions) {
      if (evaluate_expression(stuff->condition, person, 0, ({ }))->value) {
         if (stuff->type == PLAYER_CRAFT_SHOP_EXPR_DENY) {
            max_deny = evaluate_expression(stuff->value, person, 0, ({ }))->value;
         } else {
            max_accept = evaluate_expression(stuff->value, person, 0, ({ }))->value;
         }
      }
   }

   return ({ max_accept, max_deny });
} /* query_maximum_sale_value() */

/**
 * This method will return the maximum value at which the set of items
 * will be denied.
 * @param person the person selling the item
 * @param obs the set of objects
 * @return the deny value
 */
int query_maximum_sale_value(string person, object* obs) {
   int* stuff;
   object ob;
   int cur_deny;

   foreach (ob in obs) {
      stuff = query_maximum_sale_value_both(person, ob);
      if (!cur_deny && stuff[1]) {
         cur_deny = stuff[1];
      } else if (cur_deny && cur_deny > stuff[1]) {
         cur_deny = stuff[1];
      }
   }
   return cur_deny;
} /* query_maximum_sale_value() */

/** @ignore yes */
string query_extra_price_information(string seller, object *obs) {
   int* max;
   int* max_tmp;
   string ret;
   string place;
   object ob;

   max = ({ 0, 0 });
   foreach (ob in obs) {
      max_tmp = query_maximum_sale_value_both(seller, ob);
      if (max_tmp[0] && max[0] > max_tmp[0]) {
         max[0] = max_tmp[0];
      }
      if (max_tmp[1] && max[1] > max_tmp[1]) {
         max[1] = max_tmp[1];
      }
   }

   ret = "";
   place = query_property("place");
   if (!place) {
      place = "default";
   }
   if (max[0]) {
      ret += "the maximum price you can sell this straight into the "
             "inventory for is " +
             MONEY_H->money_value_string(max[0], place);
   }
   if (max[1]) {
      if (max[0]) {
         ret += " and ";
      }
      ret += "the maximum price you can sell the item for at all is " +
             MONEY_H->money_value_string(max[1], place);
   }
   return capitalize(ret) + ".\n";
} /* query_extra_price_information() */

/**
 * This method creates a save file for the specified objects autoloading
 * capability.  If the number to write to is non-null then it will
 * write to that object.
 * @param ob the object to get an autoload number for
 * @param fixed_num the file number to write to
 * @return the auto load number
 * @see save_it()
 */
protected int create_auto_load_file(mixed ob, int fixed_num) {
   mixed *auto_load;
   string tmp;

   if (objectp(ob)) {
      if (this_player()) {
         catch(auto_load = this_player()->create_auto_load(({ ob })));
      }
      if (!auto_load) {
         catch(auto_load = PLAYER_OBJ->create_auto_load(({ ob })));
      }
      if (!auto_load) {
         // Make it error on the last one...
         auto_load = AUTO_LOAD_OB->create_auto_load(({ ob }));
      }
   } else if (pointerp(ob)) {
      auto_load = ob;
   } else {
      printf("Error!  Dammit!\n");
   }
   if (!fixed_num) {
      do {
         tmp = do_read_file(CRAFT_SHOP_DATA_SAVE_FILE,
                        "app_" + _current_save_num);
         if (tmp) {
            _current_save_num++;
         }
      } while (tmp);
      fixed_num = _current_save_num;
   }
   do_save_file(CRAFT_SHOP_DATA_SAVE_FILE,
            auto_load,
            "app_" + fixed_num);
   return fixed_num;
} /* create_auto_load_file() */

/**
 * This method creates a real object from the save file number.
 * @param num the save file number
 * @return the nice shiny new object
 * @see create_auto_load_file()
 */
protected object create_real_auto_load_object(int num, object player) {
   string auto_load;
   object *obs;

   auto_load = do_read_file(CRAFT_SHOP_DATA_SAVE_FILE,
                        "app_" + num);
   if (userp(player)) {
      obs = player->load_auto_load_to_array(auto_load, player);
   } else if (this_player()) {
      obs = this_player()->load_auto_load_to_array(auto_load, player);
   } else {
      obs = PLAYER_OBJ->load_auto_load_to_array(auto_load, player);
   }
   if (sizeof(obs)) {
      return obs[0];
   }
   return clone_object("/std/object");
} /* create_object_from_auto_load() */

/**
 * This method turns all the specified id into an item to be checked.
 * @param id the id of the object to create
 * @param player the player to use for autoloading
 */
object create_checkout_object(class approval_obs approve,
                            int id,
                            object player) {
   object ob;
   int i;

   ob = create_real_auto_load_object(id, player);
   ob->add_effect("/std/effects/object/no_save");
   ob->add_property(PLAYER_CRAFT_SHOP_CHECK_PROP, ({ approve, id }));
   i = member_array(id, approve->saved);
   approve->checkout[i] = 1;
   event_save(this_object());
   _something_checkedout++;
   return ob;
} /* create_checkout_object() */

/**
 * This method updates the checked out item and changes to to be what
 * we now have...
 * @param ob the object to destroy
 */
void destroy_checkout_object(object ob) {
   int id;
   int *ids;
   int i;
   class approval_obs approve;

   if (!ob) {
      return ;
   }

   //
   // Zap the effect too.
   //
   ids = ob->effects_matching("/std/effects/object/no_save"->query_classificaton());
   foreach (id in ids) {
      ob->delete_effect(id);
   }

   id = ob->query_property(PLAYER_CRAFT_SHOP_CHECK_PROP)[1];
   approve = ob->query_property(PLAYER_CRAFT_SHOP_CHECK_PROP)[0];
   i = member_array(id, approve->saved);
   approve->checkout[i] = 0;

   ob->remove_property(PLAYER_CRAFT_SHOP_CHECK_PROP);
   create_auto_load_file(ob, id);
   event_save(this_object());
   ob->move("/room/rubbish");
   _something_checkedout--;
   if (_something_checkedout < 0) {
       _something_checkedout = 0;
    }
} /* destroy_checkout_object() */

/**
 * This method checks to see if the specified item is a checked out
 * item.
 * @param ob the object to check
 * @return 1 if it is, 0 if not
 */
int is_checkout_object(object ob) {
   return ob->query_property(PLAYER_CRAFT_SHOP_CHECK_PROP);
} /* is_checkout_object() */

/**
 * This method adds an item to the list of items that need to be approved.
 * @param seller name of the player
 * @param sellables the list of items to sell
 * @param value the cost of the item
 * @param name the name of the items
 * @param category the category of the item
 * @return obs the objects we bought
 */
object* add_to_approval_list(string seller, object* sellables, int value,
                          string name, string category) {
   class approval_obs stuff;
   object ob;
   object* obs;

   obs = ({ });
   foreach (stuff in query_approval_list()) {
      if (lower_case(stuff->name) == lower_case(name) &&
          stuff->seller == seller &&
          stuff->value == value) {
         stuff->category = category;
         foreach (ob in sellables) {
            if (ob->move("/room/rubbish") == MOVE_OK) {
               stuff->saved += ({ create_auto_load_file(ob, 0) });
               stuff->checkout += ({ 0 });
               obs += ({ ob });
            }
         }
         if (sizeof(obs)) {
            event_save(this_object());
         }
         return obs;
      }
   }


   stuff = new(class approval_obs);
   stuff->seller = seller;
   stuff->value = value;
   stuff->name = name;
   stuff->category = category;
   stuff->saved = ({ });
   stuff->enter_time = time();
   foreach (ob in sellables) {
      if (ob->move("/room/rubbish") == MOVE_OK) {
         stuff->saved += ({ create_auto_load_file(ob, 0) });
         obs += ({ ob });
      }
   }
   stuff->checkout = allocate(sizeof(stuff->saved));
   if (sizeof(obs)) {
      _waiting_for_approval += ({ stuff });
      event_save(this_object());
   }
   return obs;
} /* add_to_approval_list() */

/**
 * This method removes the item from the approval list.
 * @param approve the item to remove
 */
void remove_from_approval_list(class approval_obs approve) {
   int i;
   int pos;

   for (i = 0; i < sizeof(_waiting_for_approval); i++) {
      if (_waiting_for_approval[i] == approve) {
         //
         // Zap the files...
         //
         foreach (pos in approve->saved) {
            do_save_file(CRAFT_SHOP_REMOVE_DATA_SAVE_FILE,
                         0,
                         "app_" + pos);
         }
         _waiting_for_approval = _waiting_for_approval[0..i-1] + _waiting_for_approval[i+1..];

         event_save(this_object());
         return ;
      }
   }
} /* remove_from_approval_list() */

/**
 * This method adds in transaction into the transaction list.
 * @param person the person doing the thing
 * @param objects the string name of the objects
 * @param type the type of the transaction
 * @param amount the cost of the transaction
 * @param name_cat the name/category of the item
 * @param extra anything extra about the transaction
 */
void add_transaction(string person, object* objects, int type, int amount,
                     string name_cat, mixed extra) {
   class shop_transaction bing;
   string str;

   str = query_multiple_short(objects, 0, 1);

   bing = new(class shop_transaction);
   bing->time = time();
   bing->person = person;
   bing->objects = str;
   bing->type = type;
   bing->amount = amount;
   bing->name_cat = name_cat;
   bing->extra = extra;
   _transactions += ({ bing });
   event_save(this_object());
} /* add_transaction() */

/**
 * This method truns a transaction into a string.
 * @param trans the transaction
 * @return the transaction as a string
 */
string query_transaction_string(class shop_transaction trans) {
   string ret;
   string amt;
   string place;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   amt = MONEY_H->money_value_string(trans->amount, place);
   ret = ctime(trans->time)[4..<9] + ": " + trans->person;
   switch (trans->type) {
   case PLAYER_CRAFT_SHOP_SELL :
      ret += " sells " + trans->objects + " (" + trans->name_cat +
             ") for " + amt;
      break;
   case PLAYER_CRAFT_SHOP_SELL_AUTO :
      ret += " sells " + trans->objects + " (" + trans->name_cat +
             ") [accepted] for " + amt;
      break;
   case PLAYER_CRAFT_SHOP_APPROVE :
      ret += " approves " + trans->objects + " (" + trans->name_cat +
             ") sold by " + trans->extra + " for " + amt;
      break;
   case PLAYER_CRAFT_SHOP_BUY :
      ret += " buys " + trans->objects + " (" + trans->name_cat +
             ") sold by " + trans->extra + " for " + amt;
      break;
   }
   return ret;
} /* query_transaction_string() */

/**
 * This method is called to complete the sale completely.  It is split
 * up into a second function to allow the extra sell stuff
 * to work neatly.
 * @param value the value of the objects to sell
 * @param name the list name
 * @param sellable the list of objects to sell
 * @param category the category of the object, 0 if none
 * @ignore yes
 */
protected void complete_sale(int value, string name, object *sellable,
                             string category) {
   string place;
   object *bought;
   object money;
   int approve;
   int bing;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   //
   // Make sure we mess with the price before we do any of the approval
   // things.
   //
   bing = query_controller()->query_list_object_cost(this_player()->query_name(), name);
   if (((value * (100 + _automatic_percentage)) / 100) > bing) {
      bing = ((value * (100 + _automatic_percentage)) / 100);
   } else {
      value = (100 * bing) / (100 + _automatic_percentage);
   }

   approve = is_item_approved(this_player()->query_name(),
                        value, name, sellable, category);
   if (approve == -1) {
      write("This item is denied sale here, it is either too expensive or "
            "you are not allowed to sell items here.\n");
      say(this_player()->query_cap_name() + " finished attempting to sell " +
                query_multiple_short(sellable) + " to " + the_short() + ".\n");
      return ;
   }

   if (approve == 1) {
      bought = query_controller()->buy_objects(sellable, name, 
                                   bing,
                                   this_player()->query_cap_name(), category);
      if (sizeof(bought)) {
         add_transaction(this_player()->query_name(),
                         sellable,
                         PLAYER_CRAFT_SHOP_SELL_AUTO,
                         value * sizeof(sellable),
                         name + " in " + category,
                         0);
         if (query_owner() != this_player()->query_name()) {
            _total_outgoing += value * sizeof(sellable);
            query_controller()->adjust_royalty(query_owner(),
                                               -value * sizeof(sellable));
            event_save(this_object());
            money = MONEY_H->make_new_amount( value * sizeof(sellable), place);
            money->move(this_player());
            write("Sold " + query_multiple_short(bought) + ", listed as '" +
               name + "' " + (category?" in category " + category + " ":"") +
               "for " + MONEY_H->money_value_string(value, place) +
               ".\nYou have been paid for the items already.\n");
            say(this_player()->query_cap_name() + " sold " +
                query_multiple_short(bought) + " to " + the_short() + ".\n");
         } else {
            write("Added " + query_multiple_short(bought) + ", listed as '" +
               name + "' " + (category?" in category " + category + " ":"") +
               "for " + MONEY_H->money_value_string(value, place) +
               " to the inventory of your shop.\n");
            say(this_player()->query_cap_name() + " sold " +
                query_multiple_short(bought) + " to " + the_short() + ".\n");
         }
      }
      return ;
   }

   write("Waiting for approval from the shop owner for the items " +
         query_multiple_short(sellable) + " with the name '" +
         name + "' selling for " +
         MONEY_H->money_value_string(value, place) +
         " in category " + category + ".\nIf the item is accepted the "
         "money will be paid into your royalty pool.\n");

   bought = add_to_approval_list(this_player()->query_cap_name(), sellable,
                        value, name, category);
   if (sizeof(bought)) {
      add_transaction(this_player()->query_name(),
                   bought,
                   PLAYER_CRAFT_SHOP_SELL,
                   value * sizeof(sellable),
                   name + " in " + category,
                   0);
      if (sizeof(bought) != sizeof(sellable)) {
         write("Unable to sell " + query_multiple_short(sellable - bought) +
                  " to " + the_short() + ".\n");
      }
      say(this_player()->the_short() + " completes selling something "
       "to the shop.\n");
   } else {
      write("Unable to sell " + query_multiple_short(sellable) + " to " +
            the_short() + ".\n");
   }
} /* complete_sale() */

/**
 * This method returns the position as a two letter value.
 * @param pos the position to mangle
 * @return the letter value
 */
string query_letter_value(int pos) {
   return sprintf("%c%c", 'A' + pos / 26, 'A' + pos % 26);
} /* query_letter_value() */

/**
 * This method goes from a letter value to a number.
 * @param letter the letters
 * @return the number
 */
int query_number_value(string letter) {
   if (!strlen(letter)) {
      return -1;
   }

   letter = lower_case(letter);
   if (strlen(letter) < 2) {
      if (letter[0] >= 'a' && letter[0] <= 'z') {
         return letter[0] - 'a';
      }
   }
   if (strlen(letter) > 2) {
      return -1;
   }
   if (letter[0] >= 'a' && letter[0] <= 'z') {
      if (letter[1] >= 'a' && letter[1] <= 'z') {
         return (letter[0] - 'a') * 26 + (letter[1] - 'a');
      }
   }
   return -1;
} /* query_number_value() */

/**
 * This method returns the approval class from the string name.  THis is
 * assumed to be called from inside a command.
 * @param name the string name
 * @return the approval object
 */
class approval_obs query_approval_class(string name) {
   int pos;

   pos = query_number_value(name);
   if (pos == -1) {
      add_failed_mess(name + " is not a valid number.\n");
      return 0;
   }

   if (!sizeof(query_approval_list())) {
      add_failed_mess("There is nothing in the approval list currently.\n");
      return 0;
   }

   if (pos >= sizeof(query_approval_list())) {
      add_failed_mess("The " + name + " is out of range, must be between AA-" +
                      query_letter_value(sizeof(query_approval_list()) - 1) +
                      ".\n");
      return 0;
   }

   return query_approval_list()[pos];
} /* query_approval_class() */

/** @ignore yes */
int ownership_change(string old_owner, string new_owner) {
   if (old_owner == new_owner) {
      return 0;
   }

   //
   // Setup all the defaults.
   //
   _automatic_approval = new(class approval);
   //_automatic_approval->sellers = ({ });
   //_automatic_approval->categories = ([ ]);
   _automatic_approval->items = ([ ]);
   _automatic_approval->expressions = ({ });
   _automatic_percentage = 10;
   _transactions = ({ });
   _sell_stats = ([ ]);
   _stats_start = time();
   _sellers = ([ ]);
   _black_list = ({ });
   _buy_expression = ({ });
   _use_expression = ({ });
} /* ownership_change() */

/** @ignore yes */
void inform_of_buy(int value, object *obs, object player, string *sellers,
                   string *names, string *cats, int *values) {
   string name_cat;
   int i;
   class shop_stats stat;

   _total_ingoing += value;
   query_controller()->adjust_royalty(query_owner(),
                                       value);
   for (i = 0; i < sizeof(obs); i++) {
      name_cat = names[i] + " in " + cats[i];
      add_transaction(this_player()->query_name(),
                      obs[i..i],
                      PLAYER_CRAFT_SHOP_BUY,
                      values[i],
                      name_cat,
                      sellers[i]);
      stat = _sell_stats[sellers[i] + " - " + name_cat];
      if (!stat) {
         stat = new(class shop_stats);
      }
      stat->num_sold++;
      stat->value_sold += values[i];
      _sell_stats[sellers[i] + " - " + name_cat] = stat;
   }
} /* inform_of_buy() */

/** @ignore yes */
object* check_for_checkout(object ob) {
   object* obs;

   if (_something_checkedout) {
      //
      // Give them a look.
      //
      if (living(ob)) {
         obs = filter(deep_inventory(ob), (: is_checkout_object($1) :));
      } else {
         if (is_checkout_object(ob)) {
            obs += ({ ob });
         }
      }
      if (sizeof(obs)) {
         foreach (ob in obs) {
            destroy_checkout_object(ob);
         }
      }
      return obs;
   }
   return ({ });
} /* check_for_checkout() */

/** @ignore yes */
void event_exit(object ob, string message, object to) {
   object *obs;

   obs = check_for_checkout(ob);
   if (sizeof(obs)) {
      tell_object(ob, "You suddenly find the uncheckout items " +
                      query_multiple_short(obs) + " check themselves "
                      "back in.\n");
   }
} /* event_exit() */

/** @ignore yes */
void event_dest_me(object ob) {
   check_for_checkout(ob);
   if (_sign_ob) {
      _sign_ob->dest_me();
   }
} /* event_dest_me() */

/**
 * This method returns all the checkedout objects to the shop.
 */
void return_all_checkedout_objects() {
   object ob;
   object* obs;

   if (_something_checkedout) {
      obs = filter(deep_inventory(this_object()), (: is_checkout_object($1) :));
      if (sizeof(obs)) {
         foreach (ob in obs) {
            destroy_checkout_object(ob);
         }
      }
      tell_room(this_object(), query_multiple_short(obs) +
                " mysteriously check themselves back in.\n");
   }
} /* return_all_checkedout_objects() */

/** @ignore yes */
void dest_me() {
   ::dest_me();
} /* dest_me() */

/**
 * @ignore yes
 * In a player run shop we change this so that only allowed people can change
 * an item once it is sold.
 */
int is_able_to_change(object ob) {
   return is_allowed(this_player()->query_name());
} /* is_able_change_ob() */

/**
 * This method returns the current level of the owners money.
 */
int query_owners_money() {
   return query_controller()->query_royalty(query_owner());
} /* query_owners_money() */

/**
 * This method returns the current sign object for the shop.
 * @return the current sign object
 */
object query_sign_object() {
   object* obs;
   if (!_sign_ob && _auto_load_sign_str) {
      obs = PLAYER_OBJ->load_auto_load_to_array(_auto_load_sign_str);
      if (sizeof(obs)) {
         _sign_ob = obs[0];
         _sign_ob->reset_get();
      }
   }
   return _sign_ob;
} /* query_sign_object() */

/**
 * This method sets the current sign object.
 * @param sign the new sign object
 */
void set_sign_object(object ob) {
   if (ob) {
      _auto_load_sign_str = PLAYER_OBJ->create_auto_load(({ ob }));
      ob->reset_get();
   } else {
      _auto_load_sign_str = 0;
   }
   _sign_ob = ob;
   event_save(this_object());
} /* set_sign_object() */

/**
 * @ignore yes
 */
int do_buy(object *obs) {
   if (!evaluate_expression(_use_expression, this_player()->query_name(), 0, ({ }))->value &&
       !is_allowed_to_use_shop(this_player()->query_name())) {
      add_failed_mess("You cannot buy anything at this shop.\n");
      return 0;
   }
   return ::do_buy(obs);
} /* do_buy() */

/**
 * This method runs through the objects and checks to see what sort of levels
 * they are allowed to sell them for to this shop.
 * @return 1 on success, 0 on failure
 */
int do_check_sell(object *obs) {
   int *stuff;
   object ob;
   string ret;
   string place;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   ret = "";
   foreach (ob in obs) {
      stuff = query_maximum_sale_value_both(this_player()->query_name(), ob);
      ret += "$I$5=$C$" + the_short() + ": ";
      if (stuff[0]) {
         ret += "will be queued for more than " +
                MONEY_H->money_value_string(stuff[0], place);
      }
      if (stuff[0] && stuff[1]) {
         ret += " and ";
      }
      if (stuff[1]) {
         ret += "will be denied for more than " +
                MONEY_H->money_value_string(stuff[1], place);
      }
      ret += ".\n";
   }
   write("$P$Check Sell$P$" + ret);
   add_succeeded_mess(({ "", "$N checks the sale of $I.\n" }), obs);
   return 1;
} /* do_check_sell() */

/**
 * List the items for approval.
 */
int do_list_approval() {
   class approval_obs approve;
   int pos;
   int shown;
   int allowed;
   int checkout;
   int i;
   string place;
   string ret;
   object* obs;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   allowed = is_allowed(this_player()->query_name());

   ret = "";
   foreach (approve in query_approval_list()) {
      if (lower_case(approve->seller) == this_player()->query_name() ||
          allowed) {
         checkout = sizeof(filter(approve->checkout, (: $1 :)));

         obs = ({ });
         for (i = 0; i < sizeof(approve->saved); i++) {
            pos = approve->saved[i];
            obs += ({ create_real_auto_load_object(pos, this_player()) });
         }
         obs->move("/room/rubbish");
         ret += query_letter_value(pos) + ") " +
               approve->seller + "'s " + approve->name + " for " +
               MONEY_H->money_value_string(approve->value, place) +
               " in " + approve->category + ", " +
               sizeof(approve->saved) + " objects (" +
               query_multiple_short(obs) + ")";

         if (checkout) {
            ret += " and " + checkout + " checked out.\n";
         } else {
            ret += ".\n";
         }
         shown++;
      }
      pos++;
   }

   if (!shown) {
      add_failed_mess("No items to approve at the moment.\n");
      return 0;
   }

   write("$P$Approval list$P$The current approval items are:\n" + ret);
   return 1;
} /* do_list_approval() */

/**
 * This method returns the item to the person that started to sell it.
 * This only works before the item has been approved.
 * @param name the name of the item to return
 */
int do_return(string name) {
   object *obs;
   object *ok;
   object *here;
   object *fail;
   object *checkout;
   object money;
   class approval_obs approve;
   object ob;
   int pos;
   int i;
   int value;
   string place;

   approve = query_approval_class(name);
   if (!approve) {
      return 0;
   }

   if (lower_case(approve->seller) != this_player()->query_name()) {
      add_failed_mess("You must be the one that sold the item to return "
                      "it.\n");
      return 0;
   }

   obs = ({ });
   checkout = ({ });
   if (sizeof(filter(approve->checkout, (: $1 :))) > 0 &&
       _something_checkedout) {
      //
      // Try and return it.
      //
      return_all_checkedout_objects();
   }

   for (i = 0; i < sizeof(approve->saved); i++) {
      pos = approve->saved[i];
      if (!approve->checkout[i]) {
         obs += ({ create_real_auto_load_object(pos, this_player()) });
      } else {
         checkout += ({ create_real_auto_load_object(pos, this_player()) });
      }
   }

   if (sizeof(checkout)) {
      place = query_property("place");
      if (!place) {
         place = "default";
      }

      write("The items " + query_multiple_short(checkout) +
            " are currently checked out.  You are reimbused the amount "
            "they would have cost to sell.\n");
      value = approve->value * sizeof(checkout);
      money = MONEY_H->make_new_amount( value, place);
      money->move(this_player());
      _total_outgoing += approve->value * sizeof(checkout);
   }

   ok = ({ });
   here = ({ });
   fail = ({ });
   foreach (ob in obs) {
      if (ob->move(this_player()) == MOVE_OK) {
         ok += ({ ob });
      } else if (ob->move(this_object())) {
         here += ({ ob });
      } else {
         fail += ({ ob });
      }
   }
   remove_from_approval_list(approve);

   if (sizeof(ok)) {
      add_succeeded_mess("$N $V their $I.\n", ok);
   }
   if (sizeof(here)) {
      add_succeeded_mess("$N $V their $I and they get put on the floor.\n",
                         here);
   }
   if (sizeof(fail)) {
      add_succeeded_mess( ({ "You find that $I do not want to be moved.\n",
                             "" }), fail);
   }
   return 1;
} /* do_return() */

/**
 * This method approves the specified item.
 */
int do_approve_item(string name) {
   int cost;
   object *obs;
   class approval_obs approve;
   string place;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to approve items.\n");
      return 0;
   }

   approve = query_approval_class(name);
   if (!approve) {
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   cost = approve->value * sizeof(obs);
   if (this_player()->query_value_in(place) < cost) {
      add_failed_mess("You need at least " +
                      MONEY_H->money_value_string(cost, place) +
                      " to put this into the shop.\n");
      return 0;
   }
   if (sizeof(filter(approve->checkout, (: $1 :))) > 0 &&
       _something_checkedout) {
      //
      // Try and return it.
      //
      write("One of these objects is still checkedout, do you wish to "
            "continue this without these items? ");
      input_to("confirm_approval", 0, approve);
      add_succeeded_mess( ({ "", "$N approves something in the shop.\n" }) );
      return 1;
   }
   

   confirm_approval("y", approve);
   return 1;
} /* do_approve_item() */

protected void confirm_approval(string answer, class approval_obs approve) {
   int pos;
   int i;
   int bing;
   int cost;
   object *obs;
   object *bought;
   object *checkout;
   string place;

   if (strlen(answer) < 1 || lower_case(answer)[0] != 'y') {
      write("Ok, canceled the approval of the item.\n");
      return ;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   //
   // Approval means we create all the objects, remove them, and then
   // send them off to the main engine...
   //
   obs = ({ });
   checkout = ({ });
   for (i = 0; i < sizeof(approve->saved); i++) {
      pos = approve->saved[i];
      if (!approve->checkout[i]) {
         obs += ({ create_real_auto_load_object(pos, this_player()) });
      } else {
         checkout += ({ create_real_auto_load_object(pos, this_player()) });
      }
   }

   if (sizeof(checkout)) {
      write("The items " + query_multiple_short(checkout) + " were "
            "checked out and therefor lost.\n");
   }

   bing = query_controller()->query_list_object_cost(approve->seller, approve->name);
   if (((approve->value * (100 + _automatic_percentage)) / 100) > bing) {
      bing = ((approve->value * (100 + _automatic_percentage)) / 100);
   }
   bought = query_controller()->buy_objects(obs, approve->name, 
                                   bing,
                                   approve->seller, approve->category);
   if (sizeof(bought)) {
      cost = approve->value * (sizeof(obs) + sizeof(checkout));
      _total_outgoing += cost;
      remove_from_approval_list(approve);
      query_controller()->adjust_royalty(lower_case(approve->seller), cost);
      this_player()->pay_money(MONEY_H->create_money_array(cost, place), place);
      write("You approve " + query_multiple_short(obs) + " to be sold as " +
            approve->name + " in " + approve->category + " for " +
            MONEY_H->money_value_string(approve->value, place) + ".\n");
      add_succeeded_mess( ({ "", "$N approves something in the shop.\n" }) );
      add_transaction(this_player()->query_name(),
                      obs,
                      PLAYER_CRAFT_SHOP_APPROVE,
                      approve->value * (sizeof(obs) + sizeof(checkout)),
                      approve->name + " in " + approve->category,
                      approve->seller);
   } else {
      write("Unable to move the items into the shops inventory.  "
            "Contact a creator or error report this please.\n");
   }
   return ;
} /* do_approve_item() */

/**
 * This method rejects the specified item.
 */
int do_approve_reject(string name) {
   object *obs;
   object *ok;
   object *here;
   object *checkout;
   object ob;
   object play;
   class approval_obs approve;
   int pos;
   int i;
   int ret;

   approve = query_approval_class(name);
   if (!approve) {
      return 0;
   }

   if (lower_case(approve->seller) == this_player()->query_name()) {
      //
      // Return the stuff to them.
      //
      return do_return(name);
   }

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You're not allowed to reject items.\n");
      return 0;
   }

   
   if (sizeof(filter(approve->checkout, (: $1 :))) > 0 &&
       _something_checkedout) {
      //
      // Try and return it.
      //
      return_all_checkedout_objects();
   }

   //
   // Approval means we create all the objects, remove them, and then
   // send them off to the main engine...
   //
   obs = ({ });
   checkout = ({ });
   for (i = 0; i < sizeof(approve->saved); i++) {
      pos = approve->saved[i];
      if (!approve->checkout[i]) {
         obs += ({ create_real_auto_load_object(pos, this_player()) });
      } else {
         checkout += ({ create_real_auto_load_object(pos, this_player()) });
      }
   }

   if (member_array(0, obs) != -1) {
      add_failed_mess("There seems to be a problem with this rejection set, "
                      "one of the items is 0.\n");
      return 0;
   }

   if (sizeof(checkout)) {
      write("The items " + query_multiple_short(checkout) + " were "
            "checked out and lost.\n");
      query_controller()->adjust_royalty(lower_case(approve->seller),
                                         approve->value * sizeof(checkout));
      AUTO_MAILER->auto_mail(lower_case(approve->seller),
                            this_player()->query_name(),
                            "Rejected items", "", 
                            sprintf("%-=75s",
                                   this_player()->query_name() + " lost your " +
                                   query_multiple_short(checkout, "the", 1) +
                                   ",so you have been paid but the items "
                                   "were not returned.\n"));
   }

   if (!sizeof(obs)) {
      write("Nothing to give back or send off.\n");
      write("You reject the item.\n");
   } else {
      //
      // They are here, so give it back.
      //
      play = find_player(lower_case(approve->seller));
      if (play  &&  environment(play) == this_object()) {
         ok = ({ });
         here = ({ });
         foreach (ob in obs) {
            if (ob) {
               if (ob->move(play) == MOVE_OK) {
                  ok += ({ ob });
               } else if (ob->move(this_object()) == MOVE_OK) {
                  here += ({ ob });
               }
            }
         }
         if (sizeof(ok)) {
            tell_object(play, "Your items " +
                        query_multiple_short(obs) + " have been rejected "
                        "and placed in your inventory.\n");
         }
         if (sizeof(here)) {
            tell_object(play, "Your items " +
                        query_multiple_short(obs) + " have been rejected "
                        "and placed on the floor here.\n");
         }
         write("You reject the item.\n");
      } else {
         if (!_parcel_post) {
            add_failed_mess("Eeek!  No parcel post defined on this room.\n");
            return 0;
         }
   
         ret = _parcel_post->deposit_parcel(obs, lower_case(approve->seller), 1);
         if (ret != 1) {
            if (ret == -5) {
               write("The player " + approve->seller + " has been denied use "
                     "of the parcel post system, tossing objects away.\n");
               obs->move("/room/rubbish");
            } else if (ret == -4) {
               write("You have been denied use of the postal system, perhaps "
                     "you could try clearing this up with the creators?\n");
               obs->move("/room/rubbish");
               return 1;
            } else {
               add_failed_mess("Unable to send the parcel for some reason (" +
                            ret + ").\n");
                  obs->move("/room/rubbish");
               return 0;
            }
         }
         write("You reject the item and it is parcel posted back to the "
            "person who "
            "sent it.\nWould you like to send them a note about it as well? ");
         obs -= ({ 0 });
         input_to("check_reject_note", 0, query_multiple_short(obs, "the", 1),
                  approve->seller);
      }
   }
   remove_from_approval_list(approve);
   add_succeeded_mess(({ "", "$N reject$s a sellable object.\n" }));
   return 1;
} /* do_approve_reject() */

/**
 * @ignore yes
 */
void check_reject_note(string str, string ob_str, string name) {
   str = lower_case(str);
   if (!strlen(str) ||
       (str[0] != 'y' &&
        str[0] != 'n')) {
      write("Please answer yes or no.\nWould you like to send them "
            "a note about it as well?");
      input_to("check_reject_note", 0, ob_str, name);
      return ;
   }
   if (str[0] == 'n') {
      write("ok, bye then!\n");
      return ;
   }
   this_player()->do_edit("Your items " + ob_str + " were rejected "
                         "from " + the_short() + ".\n\n",
                         "send_reject_note", this_object(), 0, name);
} /* check_reject_note() */

/** @ignore yes */
void send_reject_note(string mess, string name) {
   if (!mess) {
      write("Aborted.\n");
      return ;
   }
   write("Sending to " + name + ".\n");
   AUTO_MAILER->auto_mail(name, this_player()->query_name(),
                            "Rejected items", "", mess);
} /* send_reject_note() */

/**
 * This method browses the specified item.
 */
int do_approve_browse(string name) {
   object *obs;
   object ob;
   string read;
   string ret;
   class approval_obs approve;
   int pos;
   string place;

   approve = query_approval_class(name);
   if (!approve) {
      return 0;
   }

   if (lower_case(approve->seller) == this_player()->query_name()) {
      //
      // Return the stuff to them.
      //
      return do_return(name);
   }

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to reject items.\n");
      return 0;
   }

   
   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   //
   // Create the objects, browse them, kill them.
   //
   obs = ({ });
   foreach (pos in approve->saved) {
      obs += ({ create_real_auto_load_object(pos, this_player()) });
   }

   ret = "";
   foreach (ob in obs) {
      ret += ob->the_short() + ": (Base cost " +
             MONEY_H->money_value_string(ob->query_base_value(), place) +
             "; scaled cost " +
             MONEY_H->money_value_string(ob->query_value(), place) +
             "\n" + ob->long();
      read = ob->query_readable_message();
      if (read) {
         ret += "You read " +
                 replace_string(ob->query_read_short(this_player()), "$name$",
                         ob->a_short()) + ":\n" +
                 ob->query_readable_message();
      }
   }

   obs->move("/room/rubbish");

   write("$P$Browse list$P$" + ret);

   add_succeeded_mess("$N browse$s an item waiting for approval.\n");
   return 1;
} /* do_approve_browse() */

/**
 * This method checks out the specified item.
 */
int do_approve_checkout(string name) {
   object *obs;
   object *bad;
   object *checkout;
   object ob;
   class approval_obs approve;
   int pos;
   int i;

   approve = query_approval_class(name);
   if (!approve) {
      return 0;
   }

   if (lower_case(approve->seller) == this_player()->query_name()) {
      //
      // Return the stuff to them.
      //
      return do_return(name);
   }

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to reject items.\n");
      return 0;
   }

   
   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   //
   // Create the objects, browse them, kill them.
   //
   obs = ({ });
   checkout = ({ });
   for (i = 0; i < sizeof(approve->saved); i++) {
      pos = approve->saved[i];
      if (!approve->checkout[i]) {
         ob = create_checkout_object(approve, pos, this_player());
         if (!ob) {
            write("Unable to create " + pos + approve->name + ", weird...\n");
         } else {
            obs += ({ ob });
         }
      } else {
         checkout += ({ create_checkout_object(approve, pos, this_player()) });
      }
   }

   checkout->move("/room/rubbish");

   if (!sizeof(obs) &&
       sizeof(checkout)) {
      if (sizeof(checkout) > 0) {
         add_failed_mess("$I are already checked out.\n", checkout);
      } else {
         add_failed_mess("$I is already checked out.\n", checkout);
      }
      return 0;
   }

   bad = ({ });
   foreach (ob in obs) {
      if (ob->move(this_player()) != MOVE_OK) {
         bad += ({ ob });
         destroy_checkout_object(ob);
      }
   }

   if (sizeof(bad)) {
      write("Unable to move " + query_multiple_short(bad) + " into your "
            "inventory for you to check.\n");
   }

   obs -= bad;
   if (sizeof(obs)) {
      write("Moved " + query_multiple_short(obs) + " into your inventory "
            "for you to check.\n");
      add_succeeded_mess("$N checks out $I.\n", obs);
   }

   return 1;
} /* do_approve_checkout() */

/**
 * This method checks in a checked out object.
 */
int do_approve_checkin(object* obs) {
   object ob;
   object* ok;

   ok = ({ });
   foreach (ob in obs) {
      if (is_checkout_object(ob)) {
         destroy_checkout_object(ob);
         ok += ({ ob });
      }
   }

   if (!sizeof(ok)) {
      add_failed_mess("None of $I have been checked out.\n", obs);
      return 0;
   }

   add_succeeded_mess("$N check$s $I back in.\n", ok);
   return 1;
} /* do_approve_checkin() */

/**
 * This method changes the name of the item to be approved.
 * @param name the reference
 * @param new_name the new name for the item
 */
int do_approve_name_change(string name, string new_name) {
   class approval_obs approve;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to approve items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   approve = query_approval_class(name);
   write("You change the name of the item waiting to be approved from " +
         approve->name + " to " + new_name + ".\n");
   approve->name = new_name;
   add_succeeded_mess( ({ "", "$N messes with the approval items.\n" }));
   event_save(this_object());
   return 1;
} /* do_approve_name_change() */

/**
 * This method changes the category of the item waiting to be approved.
 * @param name the reference
 * @param new_category the new category for the item
 */
int do_approve_category_change(string name, string new_category) {
   class approval_obs approve;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to approve items.\n");
      return 0;
   }

   approve = query_approval_class(name);
   if (!approve) {
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   new_category = query_real_category_name(new_category);
   if (!new_category ||
       !query_controller()->is_valid_category(new_category)) {
      add_failed_mess("You must choose a category that exists.\n");
      return 0;
   }

   approve->category = new_category;
   add_succeeded_mess(({
      "You change the category of the approval item " + name + ".\n",
      "$N messes with the approval items.\n" }));
   event_save(this_object());
   return 1;
} /* do_approve_category_change() */

/**
 * This method sets the automatic low acception level.
 * @param cost the cost to use a low level
 */
int do_approve_auto_low(string cost) {
   string place;
   int value;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   value = value_from_string(cost, place);
   _automatic_approval->low_cost = value;
   event_save(this_object());
   if (!value) {
      add_succeeded_mess(({ "You disable the automatic acception of items of "
                            "low value.\n",
                            "$N fiddle$s with something in the shop.\n" }) );
      return 1;
   }

   add_succeeded_mess(({ "You set the shop to automatically accept any item "
                         "costing less than " +
                         MONEY_H->money_value_string(value, place) + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_low() */

/**
 * This method sets the automatic high cut off level.
 * @param cost the cost to use a high cut off level
 */
int do_approve_auto_high(string cost) {
   string place;
   int value;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   value = value_from_string(cost, place);
   if (!value) {
      _automatic_approval->high_cost = value;
      add_succeeded_mess(({ "You disable the automatic placing items into the "
                            "approve list of items of high value.\n",
                            "$N fiddle$s with something in the shop.\n" }) );
      return 1;
   }

   _automatic_approval->high_cost = value;
   event_save(this_object());
   add_succeeded_mess(({ "You set the shop to automatically put any item "
                         "costing more than " +
                         MONEY_H->money_value_string(value, place) +
                         " into the approve list.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_high() */

/**
 * This method sets the automatic high cut off level for denying sales.
 * @param cost the cost to use a high cut off level
 */
int do_approve_auto_high_deny(string cost) {
   string place;
   int value;

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   value = value_from_string(cost, place);
   if (!value) {
      _automatic_approval->high_cost_deny = 0;
      add_succeeded_mess(({ "You disable the automatic denying of items of "
                            "high value.\n",
                            "$N fiddle$s with something in the shop.\n" }) );
      return 1;
   }

   _automatic_approval->high_cost_deny = value;
   event_save(this_object());
   add_succeeded_mess(({ "You set the shop to automatically deny any item "
                         "costing more than " +
                         MONEY_H->money_value_string(value, place) + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_high() */

/**
 * This method sets the automatic items to allow.
 * @param item the items short to allow
 */
int do_approve_auto_item_add(string item, string money) {
   int value;
   string place;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   value = value_from_string(money, place);
   if (!value) {
      add_failed_mess("The value " + money + " is invalid.\n");
      return 0;
   }

   if (_automatic_approval->items[item] == value) {
      add_failed_mess("The item " + item + " already has a cut off "
                      "value of " +
                      MONEY_H->money_value_string(value, place) + ".\n");
      return 0;
   }

   _automatic_approval->items[item] = value;
   event_save(this_object());
   add_succeeded_mess(({ "You set the shop to automatically accept any item "
                         "with a short of " + item + " up to a cost "
                         "of " + MONEY_H->money_value_string(value, place) +
                         ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_item_add() */

/**
 * This method sets the automatic items to allow.
 * @param item the items short to allow
 */
int do_approve_auto_item_add_object(object *obs, string money) {
   int value;
   string place;
   object ob;
   string name;
   object *ok;
   object *bad;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   value = value_from_string(money, place);
   if (!value) {
      add_failed_mess("The value " + money + " is invalid.\n");
      return 0;
   }

   bad = ({ });
   ok = ({ });
   foreach (ob in obs) {
      name = ob->query_short();
      if (_automatic_approval->items[name] == value) {
         bad += ({ name });
      } else {
         _automatic_approval->items[name] = value;
         ok += ({ name });
      }
   }
   event_save(this_object());
   if (sizeof(ok)) {
      add_succeeded_mess(({ "You set the shop to automatically accept any of " 
                         "$I up to a cost "
                         "of " + MONEY_H->money_value_string(value, place) +
                         ".\n",
                         "$N fiddle$s with something in the shop.\n" }), ok );
      return 1;
   } else {
      add_failed_mess("You cannot set $I to a value limit of " +
                      MONEY_H->money_value_string(value, place) +
                      " since it already has this limit.\n");
      return 0;
   }
} /* do_approve_auto_item_add() */

/**
 * This method sets the automatic items to allow.
 * @param item the items short to allow
 */
int do_approve_auto_item_add_expr(string item, string expr) {
   class parse_node* value;
   string place;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   value = parse_money_string(expr);
   if (!sizeof(value)) {
      add_failed_mess(query_last_expression_error() + ".\n");
      return 0;
   }

   _automatic_approval->items[item] = value;

   event_save(this_object());
   add_succeeded_mess(({ "You set the shop to automatically accept any item "
                         "with a short of " + item + " with an expression "
                         "of " + query_expression_string(value, 1) +
                         ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_item_add() */

/**
 * This method sets the automatic items to allow.
 * @param item the items short to allow
 */
int do_approve_auto_item_add_object_expr(object *obs, string expr) {
   class parse_node* value;
   object ob;
   string name;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   value = parse_money_string(expr);
   if (!sizeof(value)) {
      add_failed_mess(query_last_expression_error() + ".\n");
      return 0;
   }

   foreach (ob in obs) {
      name = ob->query_short();
      _automatic_approval->items[name] = value;
   }
   event_save(this_object());

   add_succeeded_mess(({ "You set the shop to automatically accept any of "
                      "$I with an expression "
                      "of " + query_expression_string(value, 1) +
                      ".\n",
                       "$N fiddle$s with something in the shop.\n" }), obs );
   return 1;
} /* do_approve_auto_item_add() */

/**
 * This method checks to see if the specified object can really be sold or
 * not automatically.
 * @param obs the objects to test
 */
int do_approve_auto_item_test(object* obs) {
   object ob;
   string place;
   mixed cost;
   int found;
   class expression_type stuff;
   class parse_node frog;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You cannot test to see if this item would be "
                      "automatically sold.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   foreach (ob in obs) {
      cost = _automatic_approval->items[ob->query_short()];
      if (cost) {
         if (pointerp(cost)) {
            frog = evaluate_expression(cost, this_player()->query_name(),
                                        0, ({ ob }))->value;
            write("$I$5=The item " + ob->the_short() + " can be sold if it "
              "costs less than " +
               query_expression_string(cost, 1) +
               " (" + MONEY_H->money_value_string(frog->value, place) + ").\n");
         } else {
            write("$I$5=The item " + ob->the_short() + " can be sold if it "
              "costs less than " +
               MONEY_H->money_value_string(cost, place) +
               " (" + ob->query_short() + ").\n");
         }
         found = 1;
      }

      foreach (stuff in _automatic_approval->expressions) {
         frog = evaluate_expression(stuff->condition, this_player()->query_name(),
                                 0, ({ ob }));
         if (frog->value) {
            if (stuff->type == PLAYER_CRAFT_SHOP_EXPR_ACCEPT) {
               frog = evaluate_expression(stuff->value,
                                      this_player()->query_name(),
                                      0, ({ ob }));
               write("$I$5=The item " + ob->the_short() + " can be sold if it "
                 "costs less than " +
                  query_expression_string(stuff->value, 1) +
                  " (" + ob->query_short() + ": " +
                  MONEY_H->money_value_string(frog->value, place) + 
                  ").\n");
            } else {
               write("$I$5=The item " + ob->the_short() + " will be denied "
                 "sale.\n");
            }
         }
      }
      if (!found) {
         write("$I$5=The item " + ob->the_short() + " will not be automatically "
               "accepted (" + ob->query_short() + ").\n");
      }
   }
   add_succeeded_mess(({ "",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_item_test() */

/**
 * This method sets the automatic items to allow.
 * @param item the item to accept
 */
int do_approve_auto_item_remove(string item) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   if (!_automatic_approval->items[item]) {
      add_failed_mess("The item " + item + " is not in the list.\n");
      return 0;
   }

   map_delete(_automatic_approval->items, item);
   event_save(this_object());
   add_succeeded_mess(({ "You remove the item " + item +
                         " from the list of automatically accepted items.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_item_remove() */

/**
 * This method sets the automatic items to allow.
 * @param item the item to accept
 */
int do_approve_auto_item_remove_object(object *obs) {
   object ob;
   object *bad;
   object *ok;
   string name;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   ok = ({ });
   bad = ({ });
   foreach (ob in obs) {
      name = ob->query_short();
      if (!_automatic_approval->items[name]) {
         bad += ({ ob });
      } else {
         ok += ({ ob });
         map_delete(_automatic_approval->items, name);
      }
   }
   event_save(this_object());
   if (sizeof(ok)) {
      add_succeeded_mess(({ "You remove $I from the list of "
                            "automatically accepted items.\n",
                         "$N fiddle$s with something in the shop.\n" }), ok );
      return 1;
   }
   add_failed_mess("None of $I are in the shops allow item list for you "
                   "to remove.\n", bad);
   return 0;
} /* do_approve_auto_item_remove() */

/**
 * This method sets the automatic expressions to allow.
 * @param expression the expressions short to allow
 */
int do_approve_auto_expression_add(string expression, string value_str,
                                   int type) {
   class parse_node* expr;
   class parse_node* value;
   class expression_type bing;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   expr = parse_boolean_string(expression);
   if (!sizeof(expr)) {
      add_failed_mess(query_last_expression_error() + ".\n");
      return 0;
   }

   if (type == PLAYER_CRAFT_SHOP_EXPR_ACCEPT) {
      value = parse_money_string(value_str);
      if (!sizeof(value)) {
         add_failed_mess(query_last_expression_error() + ".\n");
         return 0;
      }
   }

   bing = new(class expression_type);
   bing->type = type;
   bing->condition = expr;
   bing->value = value;
   _automatic_approval->expressions += ({ bing });

   event_save(this_object());
   if (type == PLAYER_CRAFT_SHOP_EXPR_ACCEPT) {
      add_succeeded_mess(({ "You set the shop to automatically accept any item "
                         "matching the expression " +
                         query_expression_string(expr, 0) + " with a cost "
                         "of " + query_expression_string(value, 0) + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   } else {
      add_succeeded_mess(({ "You set the shop to automatically deny any item "
                         "matching the expression " +
                         query_expression_string(expr, 0) + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   }
   return 1;
} /* do_approve_auto_item_add() */

/**
 * This method removes an allowed expression
 * @param id the expression id to remove
 */
int do_approve_auto_expression_remove(string idstr) {
   class expression_type expr;
   int id;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   id = query_number_value(idstr);
   if (id == -1 || id > sizeof(_automatic_approval->expressions)) {
      add_failed_mess("The id " + id + " is invalid.\n");
      return 0;
   }

   expr = _automatic_approval->expressions[id];
   _automatic_approval->expressions = _automatic_approval->expressions[0..id - 1] + 
                                      _automatic_approval->expressions[id + 1..];

   event_save(this_object());
   add_succeeded_mess(({ "You remove the expression " +
                         query_expression_string(expr->condition, 1) +
                         " cost: " +
                         query_expression_string(expr->value, 1) + 
                         " from the list of allowed expressions.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_auto_item_remove() */

/**
 * This method sets the percentage to add to the sell price.
 * @param num the number to set the percentage to
 */
int do_approve_percentage(int num) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set automatic approval items.\n");
      return 0;
   }

   if (query_owners_money() < 0) {
      add_failed_mess("You cannot do this until the shop's deficit is payed "
                      "off.\n");
      return 0;
   }

   if (num <= 0) {
      add_failed_mess("The percentage to add must be greator than 0.\n");
      return 0;
   }

   _automatic_percentage = num;
   add_succeeded_mess(({ "You set the percentage to add to the sale price "
                         "to " + _automatic_percentage + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_percentage() */

/**
 * This method sets the limit on number of items sellable by all the players.
 * @param limit the default limit
 */
int do_approve_limit(string limit) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to set any limits on selling "
                      "items.\n");
      return 0;
   }

   if (limit == "disable")  {
      _automatic_approval->num_allowed = 0;
      add_succeeded_mess(({ "You disable the default number of sold items "
                         "per person.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
      return 1;
   }
   else if (to_int(limit) > 0)  {
      _automatic_approval->num_allowed = to_int(limit);
      add_succeeded_mess(({ "You set the default number of sold items per "
                            "person to " + _automatic_approval->num_allowed + ".\n",
                            "$N fiddle$s with something in the shop.\n" }) );
      return 1;
   }
   return 0;
} /* do_approve_limit() */

/**
 * This method sets the limit per person to be a specified value.
 * @param person the person to set a limit on
 * @param value the limit to set
 */
int do_approve_limit_person_items(string name, int value) {
   class seller_information info;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to set any limits on selling "
                      "items.\n");
      return 0;
   }

   name = lower_case(name);

   if( !rank(name) ) {
      add_failed_mess("The person " + name + " does not exist.\n");
      return 0;
   }

   info = _sellers[name];
   if (!info) {
      info = new(class seller_information);
   }
   info->max_sellable = value;
   if (info->value_limit || info->max_sellable || info->deny_value_limit) {
      _sellers[name] = info;
   } else {
      map_delete(_sellers, name);
   }

   add_succeeded_mess(({ "You set the maximum number of items sellable by " +
                         name + " to " + info->max_sellable + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_limit_person_items() */

/**
 * This method sets the limit at which a person can automatically sell
 * to the shop.
 * @param person the person to set a limit on
 * @param money the limit to set it to
 */
int do_approve_limit_person_value(string name, string money) {
   class seller_information info;
   int value;
   string place;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to set any limits on selling "
                      "items.\n");
      return 0;
   }

   name = lower_case(name);

   if( !rank(name) ) {
      add_failed_mess("The person " + name + " does not exist.\n");
      return 0;
   }

   money = lower_case(money);
   if (money == "disable" || money == "0") {
      value = 0;
   } else {
      place = query_property("place");
      if (!place) {
         place = "default";
      }

      value = value_from_string(money, place);
      if (!value) {
         add_failed_mess("The value " + value + " is invalid, please use "
                         "'disable' to disable this feature for the person.\n");
         return 0;
      }
   }

   info = _sellers[name];
   if (!info) {
      info = new(class seller_information);
   }
   info->value_limit = value;
   if (info->value_limit || info->max_sellable || info->deny_value_limit) {
      _sellers[name] = info;
   } else {
      map_delete(_sellers, name);
   }

   if (value) {
      add_succeeded_mess(({ "You set the maximum cost of items automatically "
                            "approved by " +
                         name + " to " +
                         MONEY_H->money_value_string(info->value_limit, place) +
                         ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   } else {
      add_succeeded_mess(({ "You disable the maximum cost of items "
                         "automatically approved by " +
                         name + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   }
   return 1;
} /* do_approve_limit_person_value() */

/**
 * This method sets the limit at which a person can automatically sell
 * to the shop (deny limit).
 * @param person the person to set a limit on
 * @param money the limit to set it to
 */
int do_approve_limit_person_value_deny(string name, string money) {
   class seller_information info;
   int value;
   string place;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to set any limits on selling "
                      "items.\n");
      return 0;
   }

   name = lower_case(name);

   if( !rank(name) ) {
      add_failed_mess("The person " + name + " does not exist.\n");
      return 0;
   }

   money = lower_case(money);
   if (money == "disable" || money == "0") {
      value = 0;
   } else {
      place = query_property("place");
      if (!place) {
         place = "default";
      }

      value = value_from_string(money, place);
      if (!value) {
         add_failed_mess("The value " + value + " is invalid, please use "
                         "'disable' to disable this feature for the person.\n");
         return 0;
      }
   }

   info = _sellers[name];
   if (!info) {
      info = new(class seller_information);
   }
   info->deny_value_limit = value;
   if (info->deny_value_limit || info->max_sellable || info->value_limit) {
      _sellers[name] = info;
   } else {
      map_delete(_sellers, name);
   }

   if (value) {
      add_succeeded_mess(({ "You set the maximum cost of items accepted by " +
                         name + " to " +
                         MONEY_H->money_value_string(info->deny_value_limit, place) +
                         ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   } else {
      add_succeeded_mess(({ "You disable the maximum cost of items to be sold by " +
                         name + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   }
   return 1;
} /* do_approve_limit_person_value() */

/**
 * This method removes any limits set on the player.
 * @param person the person to remove the limit on
 */
int do_approve_limit_person_remove(string name) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to set remove limits "
                      "off someone.\n");
      return 0;
   }

   name = lower_case(name);

   if (!_sellers[name]) {
      add_failed_mess("The person " + name + " does not have any limits placed "
                      "on them.\n");
      return 0;
   }
   map_delete(_sellers, name);
   event_save(this_object());
   add_succeeded_mess(({ "You remove any limits set on " + name + ".\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_limit_person_remove() */

/**
 * This method adds the person to the black list.
 * @param person the person to add to the black list
 */
int do_approve_black_list_add(string name) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to add people to the black list.\n");
      return 0;
   }

   name = lower_case(name);

   if( !rank(name) ) {
      add_failed_mess("Sorry, " + name + " does not play here.\n");
      return 0;
   }

   if (is_allowed(name)) {
      add_failed_mess("This person is in the list of people allowed to use "
                      "the shop.  They cannot be put on a black list.\n");
      return 0;
   }

   if (member_array(name, _black_list) != -1) {
      add_failed_mess("The person " + name + " is already in the "
                      "black list.\n");
      return 0;
   }
   _black_list += ({ name });
   event_save(this_object());
   add_succeeded_mess(({ "You add " + name + " to the black list.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_black_list_add() */

/**
 * This method adds their ignore list to the black list
 */
int do_approve_black_list_add_ignore() {
   string *people;
   string *bad;
   string *ok;
   string name;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to add people to the black list.\n");
      return 0;
   }

   people = this_player()->query_property("ignoring");
   if (!people) {
      add_failed_mess("You are not ignoring anyone.\n");
      return 0;
   }

   bad = ({ });
   ok = ({ });
   foreach (name in people) {
      name = lower_case(name);

      if( !rank(name) ) {
         bad += ({ name });
      } else if (member_array(name, _black_list) != -1) {
         bad += ({ name });
      } else {
         _black_list += ({ name });
         ok += ({ name });
      }
   }
   event_save(this_object());
   if (!sizeof(ok)) {
      add_failed_mess("Your ignore list is already added to the black list.\n");
      return 0;
   }
                     
   add_succeeded_mess(({ "You add " + query_multiple_short(ok) + " to the "
                         "black list.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_black_list_add() */

/**
 * This method removes the person from the black list.
 * @param person the person to remove from the black list
 */
int do_approve_black_list_remove(string name) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to remove people from "
                      "the black list.\n");
      return 0;
   }

   name = lower_case(name);

   if (member_array(name, _black_list) == -1) {
      add_failed_mess("The person " + name + " is not in the black list.\n");
      return 0;
   }
   _black_list -= ({ name });
   event_save(this_object());
   add_succeeded_mess(({ "You remove " + name + " from the black list.\n",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_black_list_remove() */

/**
 * This method shows the current status of the shop.
 */
int do_approve_status() {
   string ret;
   string place;
   mixed expr;
   string str;
   string name;
   class seller_information stuff;
   string *bits;
   int pos;
   class expression_type bing;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to see the status of this store.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   ret = "";
   ret += "$I$5=The owner of the shop is " + query_owner() + ".\n";
   ret += "$I$5=The name of the shop is " + _shop_name + ".\n";
   if (query_sign_object()) {
      ret += "$I$5=The shop currently has a sign set.\n";
   } else {
      ret += "$I$5=The shop does not currently have a sign set.\n";
   }
   if (sizeof(query_allowed())) {
      ret += "$I$5=People allowed to change the shop are " +
             query_multiple_short(sort_array(query_allowed(), 0)) + ".\n";
   }
   if (sizeof(_black_list)) {
      ret += "$I$5=People denied access to the shop are ";
      ret += query_multiple_short(sort_array(_black_list, 0)) + "\n";
   } else {
      ret += "$I$0=Noone is denied access to the shop.\n";
   }
   ret += "$I$0=\n";

   ret += "The current percentage to automatically add to the buy price is: " +
          _automatic_percentage + "%\n";
   ret += "Limits on what can be sold.\n";
   ret += "Maximum number of items allowed in the shop : ";
   if (query_maximum_inventory_size()) {
      ret += query_maximum_inventory_size() + " (fixed).\n";
   } else {
      ret += "disabled (fixed).\n";
   }
   ret += "Maximum number of items allowed per player  : ";
   if (_automatic_approval->num_allowed) {
      ret += _automatic_approval->num_allowed + ".\n";
   } else {
      ret += "disabled.\n";
   }
   ret += "Automatically queue sales more than         : ";
   if (_automatic_approval->high_cost) {
      ret += MONEY_H->money_value_string(_automatic_approval->high_cost, place) +
             ".\n";
   } else {
      ret += "disabled.\n";
   }
   ret += "Automatically deny sales more than          : ";
   if (_automatic_approval->high_cost_deny) {
      ret += MONEY_H->money_value_string(_automatic_approval->high_cost_deny, place) +
             ".\n";
   } else {
      ret += "disabled.\n";
   }
   if (sizeof(_sellers)) {
      ret += "Limits set per player.\n";
      ret += sprintf("   %-15s %-15s %-20s %s\n", "Name", "Max Sellable",
                     "Approve Limit", "Deny limit (per item)");
      bits = sort_array(keys(_sellers), 0);
      foreach (name in bits) {
         stuff = _sellers[name];
         ret += sprintf("   %-15s %-15s %-20s %s\n", name,
                    (stuff->max_sellable?stuff->max_sellable+"":"disabled"),
                    (stuff->value_limit?MONEY_H->money_value_string(stuff->value_limit, place):"disabled"),
                    (stuff->deny_value_limit?MONEY_H->money_value_string(stuff->deny_value_limit, place):"disabled"));
      }
   } else {
      ret += "No limits set per player.\n";
   }

   ret += "\nAutomatically approve sales less than       : ";
   if (_automatic_approval->low_cost) {
      ret += MONEY_H->money_value_string(_automatic_approval->low_cost, place) +
             ".\n";
   } else {
      ret += "disabled.\n";
   }
   if (sizeof(_automatic_approval->items)) {
      ret += "Automatically accept sales of items         : \n";
      foreach (str, expr in _automatic_approval->items) {
         if (pointerp(expr) && intp(expr[0])) {
            map_delete(_automatic_approval->items, str);
         } else {
        
            ret += sprintf("$I$10=   %-20s max value:", "* " + str);
            if (pointerp(expr)) {
               str = query_expression_string(expr, 0);
               if (strsrch(str, "\n") != -1) {
                  ret += "\n" + str;
               } else {
                  ret += str;
               }
            } else {
               ret += MONEY_H->money_value_string(expr, place);
            }
            ret += "\n";
         }
      }
   } else {
      ret += "Automatically accept sales of               : (none setup)\n";
   }
   if (sizeof(_automatic_approval->expressions)) {
      ret += "$I$0=Automatic approval expressions (if any of these evaluate as "
             "true the item is accepted):\n";
      pos = 0;
      foreach (bing in _automatic_approval->expressions) {
         if (bing->type != PLAYER_CRAFT_SHOP_EXPR_DENY) {
            str = query_expression_string(bing->value, 0);
            if (strsrch(str, "\n") != -1) {
               str = "\n" + str;
            }
            str = " accept value: " + str;
         } else {
            str = " -- deny item";
         }
         ret += "$I$5=" + query_letter_value(pos) + ") " +
                query_expression_string(bing->condition, 0) + str + ".\n";
         pos++;
      }
   } else {
      ret += "$I$0=No automatic approval expressions setup.\n";
   }

   ret += "\n$I$0=";
   if (sizeof(_buy_expression)) {
      ret += "Buy condition: " + query_expression_string(_buy_expression, 0) +
             ".\n";
   } else {
      ret += "Anyone can buy from the shop.\n";
   }
   if (sizeof(_use_expression)) {
      ret += "Use condition: " + query_expression_string(_use_expression, 0) +
             ".\n";
   } else {
      ret += "Anyone can use the shop.\n";
   }


   ret += "\nCurrent money spent " +
          MONEY_H->money_value_string(_total_outgoing, place) +
          " and money made " +
          MONEY_H->money_value_string(_total_ingoing, place) + ".\n";
   if (query_owners_money() > 0) {
      ret += "The owner current has a cash float of " +
             MONEY_H->money_value_string(query_owners_money(), place) +
             ".\n";
   } else if (query_owners_money() < 0) {
      ret += "The owner current has a cash deficit of " +
             MONEY_H->money_value_string(-query_owners_money(), place) +
             ".\n";
   }

   write("$P$Status$P$" + ret);

   add_succeeded_mess(({ "",
                         "$N fiddle$s with something in the shop.\n" }) );
   return 1;
} /* do_approve_status() */

/**
 * This method pays off the cash deficit (if one exists).
 */
int do_approve_pay_deficit() {
   int cost;
   string place;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You are not allowed to pay off the deficit for " +
                      the_short() + ".\n");
      return 0;
   }

   if (query_owners_money() >= 0) {
      add_failed_mess("There is no deficit here to pay off.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   cost = -query_owners_money();
   if (this_player()->query_value_in(place) >= cost) {
      query_controller()->adjust_royalty(query_owner(), cost);

      this_player()->pay_money(MONEY_H->create_money_array(cost, place), place);
      add_succeeded_mess("$N pay$s off the deficit in " + the_short() + ".\n");
      return 1;

   }
   add_failed_mess("You do not have enough money to pay off the deficit "
                   "of " + MONEY_H->money_value_string(cost, place) +
                   ".\n");
   return 0;
} /* do_approve_pay_deficit() */

/**
 * This method sets up the expression to use when calculating the
 * ability to buy from the shop.
 */
int do_approve_buy_expression(string str) {
   class parse_node* expr;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to add restrictions to the shop.\n");
      return 0;
   }

   if (str == "disable")  {
      _buy_expression = ({ });
      add_succeeded_mess(({"You disable checking to see if someone "
         "can buy from the shop.\n",
         "$N fiddle$s about with " + the_short() + " a bit.\n"}));
      return 1;
   }
   expr = parse_boolean_string(str);

   if (!sizeof(expr)) {
      add_failed_mess(query_last_expression_error() + ".\n");
      return 0;
   } else {
      _buy_expression = expr;
      add_succeeded_mess(({ "You set the expression to use when checking "
                            "to see if someone can buy from the shop to " +
                            query_expression_string(expr, 1) +
                            ".\n",
                            "$N fiddle$s about with " + the_short() +
                                " a bit.\n" }));
      event_save(this_object());
   }
   return 1;
} /* do_approve_buy_expression() */

/**
 * This method sets up the expression to use when calculating the
 * ability to use the shop.
 */
int do_approve_use_expression(string str) {
   class parse_node* expr;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to add restrictions to the shop.\n");
      return 0;
   }

   if (str == "disable")  {
      _use_expression = ({ });
      add_succeeded_mess(({ "You disable checking to see if "
         "someone can use the shop.\n",
         "$N fiddle$s about with " + the_short() + " a bit.\n" }));
      return 1;
   }
   expr = parse_boolean_string(str);

   if (!sizeof(expr)) {
      add_failed_mess(query_last_expression_error() + ".\n");
      return 0;
   } else {
      _use_expression = expr;
      add_succeeded_mess(({ "You set the expression to use when checking "
                            "to see if someone can use the shop to " +
                            query_expression_string(expr, 1) +
                            ".\n",
                            "$N fiddle$s about with " + the_short() +
                                " a bit.\n" }));
      event_save(this_object());
   }
   return 1;
} /* do_approve_use_expression() */

/**
 * This method allows the player to add a new category to the list.
 * @param category the new category name
 */
int do_add_new_category(string category) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to add categories.\n");
      return 0;
   }

   if (query_controller()->is_valid_category(category)) {
      add_failed_mess("The category already exists.\n");
      return 0;
   }

   add_shop_category(category);
   add_succeeded_mess("$N add$s a new category.\n");
   return 1;
} /* do_add_new_category() */

/**
 * This method allows the player to remove a category from the list.
 * @param category the category to remove
 */
int do_remove_category(string category) {
   object* obs;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to remove categories.\n");
      return 0;
   }

   category = query_real_category_name(category);
   if (!category ||
       !query_controller()->is_valid_category(category)) {
      add_failed_mess("The category " + category + " does not exist.\n");
      return 0;
   }

   if (category == query_default_category()) {
      add_failed_mess("You cannot remove the default category.\n");
      return 0;
   }


   obs = query_controller()->query_sell_list_obs();
   obs = filter(obs, (: query_controller()->query_category_of_shop_object($1)  == $2:), category );

   if (sizeof(obs)) {
      add_failed_mess("The category '" + category + "' is not empty "
                     "and cannot be removed.\n");
      return 0;
   }

   write("Do you wish to remove the category " + category + " from " +
         the_short() + " (y/n)?");
   input_to("check_remove_category", 0, category);
   add_succeeded_mess(({ "", "$N removes a category from the shop.\n" }));
   return 1;
} /* do_remove_category() */

/**
 * This method allows the player to remove a category from the list.
 * @param category the category to rename
 * @param new_category the new name of a category
 */
int do_rename_category(string category, string new_category) {
   object* obs;
   object ob;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to rename categories.\n");
      return 0;
   }

   category = query_real_category_name(category);
   if (!category ||
       !query_controller()->is_valid_category(category)) {
      add_failed_mess("The category " + category + " does not exist.\n");
      return 0;
   }

   if (category == query_default_category()) {
      add_failed_mess("You cannot remove the default category.\n");
      return 0;
   }

   add_shop_category(new_category);
   obs = query_controller()->query_sell_list_obs();
   obs = filter(obs, (: query_controller()->query_category_of_shop_object($1)  == $2:), category );

   if (sizeof(obs)) {
      foreach (ob in obs) {
         query_controller()->change_category_of_shop_object(ob, new_category);
      }
   }
   remove_shop_category(category);

   add_succeeded_mess("$N rename$s category " + category + " to " +
                      new_category + ".\n");
   return 1;
} /* do_rename_category() */

/**
 * This method is called into to check to see if we can remove the category.
 * @param answer their answer
 * @param category the category to remove
 */
protected void check_remove_category(string answer, string category) {
   class approval_obs approve;

   if (!strlen(answer) ||
       lower_case(answer)[0] != 'y') {
      write("Aborting deleting the category " + category + ".\n");
      return ;
   }

   remove_shop_category(category);
   write("Removed the shop category " + category + ".\n");
   foreach (approve in query_approval_list()) {
      if (approve->category == category) {
         approve->category = query_default_category();
      }
   }
   event_save(this_object());
   return ;
} /* check_remove_category() */

/**
 * This method sets the default category for the place.
 * @param default_cat the default category
 */
int do_set_default_category(string default_cat) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("Your not allowed to set the default category.\n");
      return 0;
   }

   default_cat = query_real_category_name(default_cat);
   if (!default_cat ||
       !query_controller()->is_valid_category(default_cat)) {
      add_failed_mess("The category " + default_cat + " does not exist.\n");
      return 0;
   }

   set_default_category(default_cat);
   add_succeeded_mess("$N set$s the default category for " + the_short() +
                      ".\n");
   event_save(this_object());
   return 1;
} /* do_set_default_category() */

/**
 * This method lists the current categories and shows us which one is
 * currently the default.
 */
int do_list_categories() {
   string cat;
   string ret;

   ret = "";
   foreach (cat in query_controller()->query_categories()) {
      if (cat == query_default_category()) {
         ret += cat + " (default)\n";
      } else {
         ret += cat + "\n";
      }
   }

   write("$P$Category list$P$The current categories are:\n" + ret);
   add_succeeded_mess(({ "", "$N browses the categories.\n" }));
   return 1;
} /* do_list_categories() */

/**
 * This method gives the shop a name.
 */
int do_name_shop(string name) {
   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You cannot set the name of " + the_short() + ".\n");
      return 0;
   }

   set_shop_name(name);
   add_succeeded_mess("$N set$s the shop name to '" + name + "'.\n");
   return 1;
} /* do_name_shop() */

/**
 * This method shows the stats on all the sold items, the number and value
 * of each one sold.
 * @param type the type of sorting to do
 */
int do_stats_items(int type) {
   class shop_stats stat;
   string ret;
   string *bits;
   string place;
   string name;


   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You cannot read " + the_short() + "'s ledger.\n");
      return 0;
   }

   if (!sizeof(_sell_stats)) {
      add_failed_mess("Nothing has been sold in this transaction section "
                      "yet.\n");
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   switch (type) {
   case 1:
      bits = sort_array(keys(_sell_stats), (: _sell_stats[$1]->num_sold - _sell_stats[$2]->num_sold :));
      break;
   case 2:
      bits = sort_array(keys(_sell_stats), (: _sell_stats[$1]->value_sold - _sell_stats[$2]->value_sold :));
      break;
   default :
      bits = sort_array(keys(_sell_stats), 0);
      break;
   }

   ret = "Statistics for items sold since " + ctime(_stats_start) + ".\n";
   foreach (name in bits) {
      stat = _sell_stats[name];
      ret += name + ": " + stat->num_sold + " for " +
             MONEY_H->money_value_string(stat->value_sold, place) + ".\n";
   }

   write("$P$Sold stats$P$" + ret);
   add_succeeded_mess(({ "", "$N looks at the ledger.\n" }));
   return 1;
} /* do_stats_items() */

/**
 * This method shows a list of all the transactions that have occured in the
 * shop.
 */
int do_stats_transactions() {
   class shop_transaction bing;
   string ret;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You cannot read " + the_short() + "'s ledger.\n");
      return 0;
   }

   ret = "Statistics for transactions since " + ctime(_stats_start) + ".\n";
   foreach (bing in _transactions) {
      ret += "$I$5=" + query_transaction_string(bing) + "\n";
   }

   write("$P$Sold stats$P$" + ret);
   add_succeeded_mess(({ "", "$N looks at the ledger.\n" }));
   return 1;
} /* do_stats_transactions() */

/**
 * This method sets the sign you wish to use for your shop.
 * The sign will be displayed outside the shop for passer bys.
 * @param obs the sign
 */
int do_set_sign(object* obs) {
   object ob;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You cannot set the sign for the shop.\n");
      return 0;
   }

   if (sizeof(obs) > 1) {
      add_failed_mess("You must only specifiy one sign.\n");
      return 0;
   }

   if (living(obs[0])) {
      add_failed_mess("You have to wait till " + obs[0]->the_short() +
                      " dies first.\n");
      return 0;
   }

   if (obs[0]->get()) {
      add_failed_mess("Cannot set something as a sign that you cannot get.\n");
      return 0;
   }

   ob = query_sign_object();
   if (obs[0]->move("/room/rubbish") == MOVE_OK) {
      set_sign_object(obs[0]);
   } else {
      add_failed_mess("Unable to move $I off you.\n", obs[0..0]);
      return 0;
   }
   if (query_sign_object() != ob) {
      if (ob) {
         ob->set_get();
         if (ob->move(this_player()) != MOVE_OK) {
            ob->move(this_object());
         }
         add_succeeded_mess(({ "You remove the sign " +ob->the_short() +
                               " and set the sign " + obs[0]->the_short() +
                               ".\n",
                               "$N sets the sign in " + the_short() + ".\n" }));
      } else {
         add_succeeded_mess(({ "You set the sign " + obs[0]->the_short() +
                               ".\n",
                               "$N sets the sign in " + the_short() + ".\n" }));
      }
      return 1;
   } else {
      obs[0]->move(this_player());
      add_failed_mess("You cannot set the sign for some reason.\n");
      return 0;
   }
} /* do_set_sign() */

/**
 * This method removes the sign from the shop.
 */
int do_remove_sign() {
   object ob;

   if (!is_allowed(this_player()->query_name())) {
      add_failed_mess("You cannot set the sign for the shop.\n");
      return 0;
   }

   if (!query_sign_object()) {
      add_failed_mess("You canont remove the sign since there is no sign.\n");
      return 0;
   }

   ob = query_sign_object();
   ob->set_get();
   if (ob->move(this_player()) == MOVE_OK) {
      add_succeeded_mess("$N $V sign $I.\n", ({ ob }));
      set_sign_object(0);
      return 1;
   } else {
      ob->reset_get();
      add_failed_mess("You cannot remove the sign, unable to hold it?\n");
      return 0;
   }
} /* do_remove_sign() */

private int variable_player_level(string seller, int cost, object* ob) {
   return PLAYER_H->test_level(seller);
} /* variable_player_level() */

private string variable_player_deity(string seller, int cost, object* ob) {
   string deity;

   deity = PLAYER_H->test_deity(seller);
   if (deity) {
      return deity;
   }
   return "";
} /* variable_player_deity() */

private string variable_player_family(string seller, int cost, object* ob) {
   string family;

   family = PLAYER_H->test_family(seller);
   if (family) {
      return family;
   }
   return "";
} /* variable_player_family() */

private int variable_object_base_value(string seller, int cost, object* obs) {
   int value;
   object ob;
   int tmp;

   value = 1000000000;
   foreach (ob in obs) {
      tmp = ob->query_base_value();
      if (tmp < value) {
         value = tmp;
      }
   }
   return value;
} /* variable_object_base_value() */

private int variable_object_value(string seller, int cost, object* obs) {
   int value;
   object ob;
   int tmp;

   value = 1000000000;
   foreach (ob in obs) {
      tmp = ob->query_value();
      if (tmp < value) {
         value = tmp;
      }
   }
   return value;
} /* variable_object_value() */

private int variable_object_condition(string seller, int cost, object* obs) {
   int cond;
   object ob;
   int tmp;
   int max;

   cond = 100;
   foreach (ob in obs) {
      tmp = ob->query_cond();
      max = ob->query_max_cond();
      tmp = tmp * 100 / max;
      if (tmp < cond) {
         cond = tmp;
      }
   }
   return cond;
} /* variable_object_condition() */

private int variable_object_enchant(string seller, int cost, object* obs) {
   int enchant;
   object ob;
   int tmp;
   int max;

   if (!sizeof(obs)) {
      return 0;
   }

   enchant = 100;
   foreach (ob in obs) {
      tmp = ob->query_enchant();
      max = ob->query_max_enchant();
      tmp = tmp * 100 / max;
      if (tmp < enchant) {
         enchant = tmp;
      }
   }
   return enchant;
} /* variable_object_enchant() */

private string variable_object_type(string seller, int cost, object* obs) {
   string type;
   string old_type;
   object ob;

   if (!sizeof(obs)) {
      return 0;
   }

   //
   // First check to see if any of the owners can actually detect this.
   //
   foreach (ob in obs) {
      if (ob->query_weapon()) {
         type = "weapon";
      } else if (ob->query_armour()) {
         type = "armour";
      } else {
         type = "misc";
      }
      if (old_type && old_type != type) {
         type = "mixed";
      }
      old_type = type;
   }

   return type;
} /* variable_object_type() */

private int function_object_contains_spell(string spell_name,
                          string seller, int cost, object* obs) {
   object ob;

   if (!sizeof(obs)) {
      return 0;
   }

   foreach (ob in obs) {
      if ( !ob->query_magic_scroll() ) {
         return 0;
      }
      if ( lower_case(ob->query_spell_name()) != lower_case(spell_name) ) {
         return 0;
      }
   }
   return 1;
} /* variable_object_contains_spell() */

private int function_object_percentage_liquid(string liquid_name,
                          string seller, int cost, object* obs) {
   object ob;
   object* liquids;
   object liq;
   int volume;
   int found;
   int tmp;

   if (!sizeof(obs)) {
      return 0;
   }

   volume = 100;
   foreach (ob in obs) {
      //
      // See what liquids are in the object
      //
      liquids = filter(all_inventory(ob), (: $1->query_liquid() :));
      if (sizeof(liquids))  {
         foreach (liq in liquids) {
            if (lower_case(liq->query_short()) == lower_case(liquid_name)) {
               tmp = liq->query_weight() * 100 / ob->query_max_weight();
               if (tmp < volume) {
                  volume = tmp;
                  found = 1;
               }
            }
         }
      }
      // Problem: some things (like healing vials) don't actually have
      // anything in them.  So we have to do something different.
      else if (lower_case(ob->query_liquid_short()) ==
               lower_case(liquid_name))
      {
         tmp = ob->query_water_volume() * 100 / ob->query_max_volume();
         if (tmp < volume)  {
            volume = tmp;
            found = 1;
         }
      }
   }
   if (found) {
      return volume;
   }
   return 0;
} /* function_object_percentage_liquid() */

private int function_object_matching(string match,
                          string seller, int cost, object* obs) {
/*
   class obj_match result;

   result = match_object_in_array(match, obs, 0, find_player(seller));
   if (result->result == OBJ_PARSER_SUCCESS &&
       sizeof(result->objects) == sizeof(obs)) {
      return 1;
   }
 */
   return 0;
} /* function_object_percentage_liquid() */

private int function_object_short(string match, int all_matching,
                          string seller, int cost, object* obs) {
   string* bits;
   object* result;

   bits = explode(match, ",");
   result = filter(obs, (: member_array($1->query_short(), $2) != -1 :),
                   bits);
   if ((all_matching && sizeof(result) == sizeof(obs)) ||
       (!all_matching && sizeof(result))) {
      return 1;
   }
   return 0;
} /* function_object_percentage_liquid() */

private int function_club_member(string club, string seller, int cost, object* ob) {
   return CLUB_HANDLER->is_member_of(club, seller);
} /* function_club_member() */

void init() {
   add_command("approve", "item <string'id'>", (: do_approve_item($4[0]) :));
   add_command("approve", "list", (: do_list_approval() :));
   add_command("approve", "change name <string'id'> to <string'name'>",
               (: do_approve_name_change($4[0], $4[1]) :));
   add_command("approve", "change category <string'id'> to <string'category'>",
               (: do_approve_category_change($4[0], $4[1]) :));
   add_command("approve", "reject <string'id'>",
               (: do_approve_reject($4[0]) :));
/*
   add_command("approve", "reject <string'id'> <number'position'>",
               (: do_approve_reject_num($4[0], $4[1]) :));
 */
   add_command("approve", "auto low <string'cost'>",
               (: do_approve_auto_low($4[0]) :));
   add_command("approve", "auto expression accept add <string'condition'> cost <string'value'>",
               (: do_approve_auto_expression_add($4[0], $4[1],
                            PLAYER_CRAFT_SHOP_EXPR_ACCEPT) :));
   add_command("approve", "auto expression deny add <string'condition'>",
               (: do_approve_auto_expression_add($4[0], 0,
                            PLAYER_CRAFT_SHOP_EXPR_DENY) :));
   add_command("approve", "auto expression remove <string'id'>",
               (: do_approve_auto_expression_remove($4[0]) :));
   add_command("approve", "percentage <number'amount'>",
               (: do_approve_percentage($4[0]) :));
   add_command("approve", "auto item add <string'name'> up to <string'value'>",
               (: do_approve_auto_item_add($4[0], $4[1]) :));
   add_command("approve", "auto item add <string'name'> with <string'expression'>",
               (: do_approve_auto_item_add_expr($4[0], $4[1]) :));
   add_command("approve", "auto item object <indirect:object> up to <string'value'>",
               (: do_approve_auto_item_add_object($1, $4[1]) :));
   add_command("approve", "auto item object <indirect:object> with <string'expression'>",
               (: do_approve_auto_item_add_object_expr($1, $4[1]) :));
   add_command("approve", "auto item remove <string'name'>",
               (: do_approve_auto_item_remove($4[0]) :));
   add_command("approve", "auto item remove object <indirect:object>",
               (: do_approve_auto_item_remove_object($1) :));
   add_command("approve", "auto item test <indirect:object>",
               (: do_approve_auto_item_test($1) :));
   add_command("approve", "limit approve value <string'cost'>",
               (: do_approve_auto_high($4[0]) :));
   add_command("approve", "limit deny value <string'cost'>",
               (: do_approve_auto_high_deny($4[0]) :));
   add_command("approve", "limit number <string'max sellable|disable'>",
               (: do_approve_limit($4[0]) :));
   add_command("approve", "limit number for <string'player'> to <number'max sellable'>",
               (: do_approve_limit_person_items($4[0], $4[1]) :));
   add_command("approve", "limit approve value for <string'player'> to <string'cost|disable'>",
               (: do_approve_limit_person_value($4[0], $4[1]) :));
   add_command("approve", "limit deny value for <string'player'> to <string'cost|disable'>",
               (: do_approve_limit_person_value_deny($4[0], $4[1]) :));
   add_command("approve", "limit remove <string'player'>",
               (: do_approve_limit_person_remove($4[0]) :));
   add_command("approve", "black list add <string'player'>",
               (: do_approve_black_list_add($4[0]) :));
   add_command("approve", "black list add ignore",
               (: do_approve_black_list_add_ignore() :));
   add_command("approve", "black list remove <string'player'>",
               (: do_approve_black_list_remove($4[0]) :));
   add_command("approve", "status",
               (: do_approve_status() :));
   add_command("approve", "browse <string'id'>",
               (: do_approve_browse($4[0]) :));
   add_command("approve", "checkout <string'id'>",
               (: do_approve_checkout($4[0]) :));
   add_command("approve", "checkin <indirect:object>",
               (: do_approve_checkin($1) :));
   add_command("approve", "pay deficit",
               (: do_approve_pay_deficit() :));
   add_command("approve", "buy expression <string'expression|disable'>",
               (: do_approve_buy_expression($4[0]) :));
   add_command("approve", "use expression <string'expression|disable'>",
               (: do_approve_use_expression($4[0]) :));

   add_command("name", "shop <string'shop name'>",
               (: do_name_shop($4[0]) :));

   add_command("sign", "set <indirect:object'sign'>",
               (: do_set_sign($1) :) );
   add_command("sign", "remove",
               (: do_remove_sign() :) );

   add_command("stats", "items by name", (: do_stats_items(0) :));
   add_command("stats", "items by number", (: do_stats_items(1) :));
   add_command("stats", "items by value", (: do_stats_items(2) :));
   add_command("stats", "transactions", (: do_stats_transactions() :));

   add_command("check", "sell <indirect:object>",
                (: do_check_sell($1) :));

   add_command("category", "list", (: do_list_categories() :));
   add_command("category", "default <string'category'>",
               (: do_set_default_category($4[0]) :));
   add_command("category", "add <string'category'>",
               (: do_add_new_category($4[0]) :));
   add_command("category", "remove <string'category'>",
               (: do_remove_category($4[0]) :));
   add_command("category", "rename <string'category'> to name <string'category'>",
               (: do_rename_category($4[0], $4[1]) :));

   add_command("retrieve", "<string'id'>", (: do_return($4[0]) :) );
   add_command("return", "<string'id'>", (: do_return($4[0]) :) );

   add_command("value", "<indirect:object>",
               (: do_approve_auto_item_test($1) :));
   ::init();
} /* init() */