LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/color/
LOP/councils/
LOP/deity/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006 - 2012 by: the LoP team.                                     *
 *---------------------------------------------------------------------------*
 *                            Bank Support                                   *
 *****************************************************************************/
/*
 * Can create/delete an account.
 * Can withdraw/deposit into an account.
 * Can transfer gold from your account to another.
 * Accounts can have other characters share it.
 * Memory Cleaned up on exit.
 * Bank data is saved and loaded.
 */
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "h/mud.h"
#include "h/sha256.h"
#include "h/bank.h"

BANK_DATA *first_bank, *last_bank;

#define MGOLD   2000000000

/* Have to handle the max double gold a bit different */
/* Somewhere between 10 sextillion and 100 sextillion the double quits being as accurate as you would want */
double maxdoublegold = 0.0;
void setmaxdoublegold( void )
{
   maxdoublegold += 10000;
   maxdoublegold *= 1000000000;
   maxdoublegold *= 1000000000;
}

/* This is set up to show how much gold a character has */
char *show_char_gold( CHAR_DATA *ch )
{
   if( !ch )
      return (char *)"";

   return num_punct( ch->gold );
}

char *show_bank_balance( BANK_DATA *bank )
{
   if( !bank )
      return (char *)"";

   return double_punct( bank->balance );
}

bool has_gold( CHAR_DATA *ch, int amount )
{
   if( !ch )
      return false;

   if( ch->gold >= amount )
      return true;

   return false;
}

bool has_balance( BANK_DATA *bank, int amount )
{
   if( !bank )
      return false;

   if( bank->balance >= amount )
      return true;

   return false;
}

/* Used to see if the character can hold that much gold */
bool can_hold_gold( CHAR_DATA *ch, int amount )
{
   if( !ch )
      return false;

   if( ( ch->gold + amount ) > MGOLD || ( ch->gold + amount ) < ch->gold )
      return false;

   return true;
}

bool can_hold_balance( BANK_DATA *bank, int amount )
{
   if( !bank )
      return false;

   if( ( bank->balance + amount ) > maxdoublegold || ( bank->balance + amount ) < bank->balance )
      return false;

   return true;
}

void set_gold( CHAR_DATA *ch, int amount )
{
   if( !ch )
      return;

   amount = URANGE( 0, amount, MGOLD );

   ch->gold = amount;
}

/*
 * Only return true if it fully handles the increase.
 */
bool increase_gold( CHAR_DATA *ch, int amount )
{
   if( !ch )
      return false;

   /* Protect to limit it to the max and we are increasing so if it goes - it was way to high so put to max */
   if( ( ch->gold + amount ) >= MGOLD || ( ch->gold + amount ) < ch->gold )
   {
      send_to_char( "You're now carrying as much gold as you can handle.\r\n", ch );
      ch->gold = MGOLD;
      return false;
   }

   ch->gold += amount;

   return true;
}

/*
 * Always take into consideration that bamount and amount will change as they are transfered in increase_big_num
 * so if you wish to show them do so before sending to increase_balance.
 * Only return true if it fully handles the increase.
 */
bool increase_balance( CHAR_DATA *ch, BANK_DATA *bank, int amount )
{
   if( !bank )
      return false;

   if( bank->balance >= maxdoublegold )
   {
      if( ch )
         send_to_char( "The account already has as much gold as it can handle.\r\n", ch );
      return false;
   }

   /* Protect to limit it to the max and we are increasing so if it goes - it was way to high so put to max */
   if( ( bank->balance + amount ) >= maxdoublegold || ( bank->balance + amount ) < bank->balance )
   {
      if( ch )
         send_to_char( "The account now has as much gold as it can handle.\r\n", ch );
      bank->balance = maxdoublegold;
      return false;
   }

   bank->balance += amount;
   return true;
}

void decrease_gold( CHAR_DATA *ch, int amount )
{
   if( !ch )
      return;

   ch->gold -= amount;

   if( ch->gold < 0 )
      ch->gold = 0;
}

void decrease_balance( BANK_DATA *bank, int amount )
{
   if( !bank )
      return;

   bank->balance -= amount;

   if( bank->balance < 0 )
      bank->balance = 0.0;
}

void free_shared( SHARE_DATA *shared )
{
   STRFREE( shared->name );
   DISPOSE( shared );
}

void free_transaction( TRANSACTION_DATA *trans )
{
   STRFREE( trans->transaction );
   DISPOSE( trans );
}

void remove_shared( BANK_DATA *bank, SHARE_DATA *shared )
{
   UNLINK( shared, bank->first_share, bank->last_share, next, prev );
}

void remove_transaction( BANK_DATA *bank, TRANSACTION_DATA *trans )
{
   UNLINK( trans, bank->first_transaction, bank->last_transaction, next, prev );
}

void free_all_shared( BANK_DATA *bank )
{
   SHARE_DATA *shared, *shared_next;

   for( shared = bank->first_share; shared; shared = shared_next )
   {
      shared_next = shared->next;
      remove_shared( bank, shared );
      free_shared( shared );
   }
}

void free_all_transactions( BANK_DATA *bank )
{
   TRANSACTION_DATA *trans, *trans_next;

   for( trans = bank->first_transaction; trans; trans = trans_next )
   {
      trans_next = trans->next;
      remove_transaction( bank, trans );
      free_transaction( trans );
   }
}

void add_shared( BANK_DATA *bank, SHARE_DATA *shared )
{
   LINK( shared, bank->first_share, bank->last_share, next, prev );
}

void add_transaction( BANK_DATA *bank, TRANSACTION_DATA *trans )
{
   LINK( trans, bank->first_transaction, bank->last_transaction, next, prev );
}

void free_bank( BANK_DATA *bank )
{
   STRFREE( bank->account );
   STRFREE( bank->created );
   free_all_shared( bank );
   free_all_transactions( bank );
   DISPOSE( bank );
}

void remove_bank( BANK_DATA *bank )
{
   UNLINK( bank, first_bank, last_bank, next, prev );
}

void free_all_banks( void )
{
   BANK_DATA *bank, *bank_next;

   for( bank = first_bank; bank; bank = bank_next )
   {
      bank_next = bank->next;
      remove_bank( bank );
      free_bank( bank );
   }
}

void add_bank( BANK_DATA *bank )
{
   LINK( bank, first_bank, last_bank, next, prev );
}

void save_banks( void )
{
   BANK_DATA *bank;
   SHARE_DATA *shared;
   TRANSACTION_DATA *trans;
   FILE *fp;

   if( !first_bank )
   {
      remove_file( BANK_FILE );
      return;
   }
   if( !( fp = fopen( BANK_FILE, "w" ) ) )
   {
      bug( "%s: Can't open %s for writing.", __FUNCTION__, BANK_FILE );
      perror( BANK_FILE );
      return;
   }
   for( bank = first_bank; bank; bank = bank->next )
   {
      fprintf( fp, "%s", "#BANK\n" );
      fprintf( fp, "Createdby %s~\n", bank->created );
      fprintf( fp, "Account   %s~\n", bank->account );
      fprintf( fp, "NBalance  %.f\n", bank->balance );
      for( shared = bank->first_share; shared; shared = shared->next )
         if( shared->name )
            fprintf( fp, "Shared    %s~\n", shared->name );
      for( trans = bank->first_transaction; trans; trans = trans->next )
         if( trans->transaction )
            fprintf( fp, "Trans     %s~\n", trans->transaction );
      fprintf( fp, "%s", "End\n\n" );
   }
   fprintf( fp, "%s", "#END\n" );
   fclose( fp );
   fp = NULL;
}

BANK_DATA *new_bank( void )
{
   BANK_DATA *bank = NULL;

   CREATE( bank, BANK_DATA, 1 );
   if( !bank )
   {
      bug( "%s: bank is NULL after CREATE.", __FUNCTION__ );
      return NULL;
   }
   bank->created = NULL;
   bank->account = NULL;
   bank->first_share = bank->last_share = NULL;
   bank->first_transaction = bank->last_transaction = NULL;
   bank->balance = 0.0;
   return bank;
}

void fread_bank( FILE *fp )
{
   BANK_DATA *bank;
   const char *word;
   bool fMatch;

   bank = new_bank( );

   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = true;
            fread_to_eol( fp );
            break;

         case 'E':
            if( !str_cmp( word, "End" ) )
            {
               add_bank( bank );
               return;
	    }
	    break;

         case 'A':
            KEY( "Account", bank->account, fread_string( fp ) );
            break;

         case 'B':
            if( !str_cmp( word, "BBalance" ) )
            {
               double check;
               int bbalance = fread_number( fp );

               check = 0.0;
               check += bbalance;
               check *= 1000000000;
               bank->balance += check; /* Billions */
               fMatch = true;
               break;
            }
            if( !str_cmp( word, "Balance" ) )
            {
               int balance = fread_number( fp );

               bank->balance += balance; /* Under a billion */
               fMatch = true;
               break;
            }
            break;

         case 'C':
            KEY( "Createdby", bank->created, fread_string( fp ) );
            break;

         case 'M':
            if( !str_cmp( word, "MBalance" ) )
            {
               double check;
               int mbalance = fread_number( fp );

               check = 0.0;
               check += mbalance;
               check *= 1000000;
               bank->balance += check; /* Millions */
               fMatch = true;
               break;
            }
            break;

         case 'N':
            KEY( "NBalance", bank->balance, fread_double( fp ) );
            break;

         case 'S':
            if( !str_cmp( word, "Shared" ) )
            {
               SHARE_DATA *shared;

               CREATE( shared, SHARE_DATA, 1 );
               shared->name = fread_string( fp );
               add_shared( bank, shared );
               fMatch = true;
               break;
            }
            break;

         case 'T':
            if( !str_cmp( word, "Trans" ) )
            {
               TRANSACTION_DATA *trans;

               CREATE( trans, TRANSACTION_DATA, 1 );
               trans->transaction = fread_string( fp );
               add_transaction( bank, trans );
               fMatch = true;
               break;
            }
            break;
      }

      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   free_bank( bank );
}

void load_banks( void )
{
   FILE *fp;

   first_bank = last_bank = NULL;
   if( !( fp = fopen( BANK_FILE, "r" ) ) )
      return;
   for( ;; )
   {
      char letter;
      char *word;

      letter = fread_letter( fp );
      if( letter == '*' )
      {
         fread_to_eol( fp );
         continue;
      }
      if( letter != '#' )
      {
         bug( "%s: # not found.", __FUNCTION__ );
         break;
      }
      word = fread_word( fp );
      if( !str_cmp( word, "BANK" ) )
      {
         fread_bank( fp );
         continue;
      }
      else if( !str_cmp( word, "END" ) )
         break;
      else
      {
         bug( "%s: bad section (%s).", __FUNCTION__, word );
         fread_to_eol( fp );
         continue;
      }
   }
   fclose( fp );
   fp = NULL;
}

typedef enum
{
   BANK_DELETE, BANK_SHARE, BANK_TRANSFER, BANK_BALANCE, BANK_WITHDRAW, BANK_DEPOSIT, BANK_CREATE
} bank_types;

BANK_DATA *find_bank( char *account )
{
   BANK_DATA *bank;

   for( bank = first_bank; bank; bank = bank->next )
   {
      if( !str_cmp( bank->account, account ) )
         return bank;
   }
   return NULL;
}

CHAR_DATA *find_banker( CHAR_DATA *ch )
{
   CHAR_DATA *banker = NULL;

   for( banker = ch->in_room->first_person; banker; banker = banker->next_in_room )
   {
      if( is_npc( banker ) && xIS_SET( banker->act, ACT_BANKER ) )
         break;
   }
   return banker;
}

bool can_access_bank( BANK_DATA *bank, CHAR_DATA *ch )
{
   SHARE_DATA *shared;

   /* Creater of the account naturally has access */
   if( !str_cmp( bank->created, ch->name ) )
      return true;
   for( shared = bank->first_share; shared; shared = shared->next )
   {
      /* If they are in the shared then they have access */
      if( !str_cmp( shared->name, ch->name ) )
         return true;
   }
   /* Well made it this far so naturally they can't access it */
   return false;
}

void modify_shared( CHAR_DATA *ch, BANK_DATA *bank, char *argument )
{
   SHARE_DATA *shared;
   char arg[MIL];
   bool adding, save = false;

   /* Only allow the creator to change who has access to the account */
   if( str_cmp( bank->created, ch->name ) )
   {
      send_to_char( "Sorry, but only the creator of the account can change who has access to it.\r\n", ch );
      return;
   }

   /* See if they have access to it already and they aren't the leader */
   while( argument && argument[0] != '\0' )
   {
      adding = true; /* Lets assume we will be adding at first */
      argument = one_argument( argument, arg );

      /* If the arg is the creator just ignore it so it doesn't get put in as shared */
      if( !str_cmp( bank->created, arg ) )
         continue;

      for( shared = bank->first_share; shared; shared = shared->next )
      {
         /* Ok it was in the list so lets remove it */
         if( !str_cmp( shared->name, arg ) )
         {
            remove_shared( bank, shared );
            free_shared( shared );
            adding = false;
            save = true;
            ch_printf( ch, "%s is no longer able to access the account.\r\n", arg );
            break; /* Break out the for loop */
         }
      }
      /* Didn't find one so lets add it */
      if( adding )
      {
         /* Check for a valid pfile */
         if( !valid_pfile( arg ) )
         {
            ch_printf( ch, "%s is not a valid player and can't be added.\r\n", arg );
            continue;
         }
         CREATE( shared, SHARE_DATA, 1 );
         shared->name = STRALLOC( arg );
         add_shared( bank, shared );
         save = true;
         ch_printf( ch, "%s is now able to access the account.\r\n", arg );
      }
   }
   if( save )
      save_banks( );
   else
      send_to_char( "Nothing changed in the account.\r\n", ch );
}

void new_transaction( BANK_DATA *bank, char *string )
{
   TRANSACTION_DATA *trans;
   struct timeval now_time;
   char buf[MSL];
   int count = 0;

   /* Update time. */
   gettimeofday( &now_time, NULL );
   current_time = ( time_t ) now_time.tv_sec;
   current_time += ( time_t ) TIME_MODIFY;

   snprintf( buf, sizeof( buf ), "%s %s", distime( current_time ), string );
   CREATE( trans, TRANSACTION_DATA, 1 );
   STRSET( trans->transaction, buf );
   add_transaction( bank, trans );

   for( trans = bank->first_transaction; trans; trans = trans->next )
      ++count;
   while( count > 20 )
   {
      trans = bank->first_transaction;
      remove_transaction( bank, trans );
      free_transaction( trans );
      --count;
   }
}

void give_interest( void )
{
   BANK_DATA *bank = NULL;
   char buf[MSL];
   double interest = 0.0;

   for( bank = first_bank; bank; bank = bank->next )
   {
      if( bank->balance <= 0 )
         continue;

      interest += ( bank->balance * .0003 );
      if( interest > 0 )
      {
         if( ( bank->balance + interest ) > maxdoublegold )
         {
            interest = maxdoublegold;
            interest -= bank->balance;
            if( interest > 0 )
            {
               snprintf( buf, sizeof( buf ), "Gained %s interest.", double_punct( interest ) );
               new_transaction( bank, buf );
               bank->balance = maxdoublegold;
            }
         }
         else
         {
            snprintf( buf, sizeof( buf ), "Gained %s interest.", double_punct( interest ) );
            new_transaction( bank, buf );
            bank->balance += interest;
         }
      }
   }
   save_banks( );
}

void handle_bank( CHAR_DATA *ch, char *account, short type, int amount, char *taccount, bool uall )
{
   CHAR_DATA *banker = NULL;
   BANK_DATA *bank = NULL, *tbank = NULL;
   TRANSACTION_DATA *trans;
   char buf[MSL];

   if( !ch )
      return;

   if( !( banker = find_banker( ch ) ) )
   {
      send_to_char( "You aren't at a banker.\r\n", ch );
      return;
   }

   /* Other wise we should be doing something to balance */
   if( type != BANK_SHARE && type != BANK_DELETE && type != BANK_BALANCE && amount <= 0 && !uall )
   {
      send_to_char( "You need to use a number above 0 or all.\r\n", ch );
      return;
   }

   if( ( bank = find_bank( account ) ) && type == BANK_CREATE )
   {
      send_to_char( "An account by that name already exist.\r\n", ch );
      return;
   }

   if( !bank && type != BANK_CREATE )
   {
      send_to_char( "Invalid account.\r\n", ch );
      return;
   }

   /* Create a new account with amount deposit */
   if( type == BANK_CREATE )
   {
      if( uall )
         amount = ch->gold;
      else
      {
         if( amount <= 0 )
         {
            send_to_char( "You need to at least deposit something to open an account.\r\n", ch );
            return;
         }
         if( !has_gold( ch, amount ) )
         {
            send_to_char( "You don't have that much gold.\r\n", ch );
            return;
         }
      }
      if( !( bank = new_bank( ) ) )
         return;

      bank->created = STRALLOC( ch->name );
      bank->account = STRALLOC( account );

      decrease_gold( ch, amount );
      increase_balance( ch, bank, amount );
      snprintf( buf, sizeof( buf ), "%s opened the account with %s gold.", ch->name, num_punct( amount ) );
      new_transaction( bank, buf );
      add_bank( bank );
      save_banks( );
      save_char_obj( ch );
      ch_printf( ch, "Account %s has been created and has a balance of %s gold.\r\n", bank->account, show_bank_balance( bank ) );
      return;
   }

   /* Deposit amount */
   if( type == BANK_DEPOSIT )
   {
      if( uall )
         amount = ch->gold;
      else
      {
         if( amount <= 0 )
         {
            send_to_char( "You can't deposit 0 or less gold.\r\n", ch );
            return;
         }
         if( !has_gold( ch, amount ) )
         {
            send_to_char( "You can't deposit more gold then you have.\r\n", ch );
            return;
         }
      }

      if( !can_hold_balance( bank, amount ) )
      {
         ch_printf( ch, "Account %s can't hold that much gold.\r\n", bank->account );
         return;
      }

      decrease_gold( ch, amount );
      increase_balance( ch, bank, amount );
      snprintf( buf, sizeof( buf ), "%s deposited %s gold.", ch->name, num_punct( amount ) );
      new_transaction( bank, buf );

      save_banks( );
      save_char_obj( ch );

      ch_printf( ch, "You have deposited %s gold", num_punct( amount ) );
      if( can_access_bank( bank, ch ) )
         ch_printf( ch, " and now have %s gold on account %s", show_bank_balance( bank ), bank->account );
      send_to_char( ".\r\n", ch );
      return;
   }

   /* Do they have access to this account? */
   if( !can_access_bank( bank, ch ) )
   {
      ch_printf( ch, "You don't have permission to do anything with account %s.\r\n", bank->account );
      return;
   }

   /* Modify who all else can access this account */
   if( type == BANK_SHARE )
   {
      modify_shared( ch, bank, taccount );
      return;
   }

   /* Check balance */
   if( type == BANK_BALANCE )
   {
      ch_printf( ch, "Account %s has %s gold.\r\n", bank->account, show_bank_balance( bank ) );

      for( trans = bank->first_transaction; trans; trans = trans->next )
      {
         if( trans == bank->first_transaction )
            send_to_char( "Last 20 Transactions.\r\n", ch );
         ch_printf( ch, "%s\r\n", trans->transaction );
      }
      return;
   }

   /* Delete account */
   if( type == BANK_DELETE )
   {
      /* Only allow the creator to delete the account, since all money in it is lost */
      if( str_cmp( bank->created, ch->name ) )
      {
         send_to_char( "Sorry, but only the creator of the account can delete it.\r\n", ch );
         return;
      }

      ch_printf( ch, "Account %s has been deleted.\r\n", bank->account );
      remove_bank( bank );
      free_bank( bank );
      save_banks( );
      return;
   }

   /* Withdraw amount */
   if( type == BANK_WITHDRAW )
   {
      if( uall )
      {
         if( bank->balance > MGOLD )
         {
            send_to_char( "You can't hold that much gold.\r\n", ch );
            return;
         }
         amount = (int)bank->balance;
      }
      else
      {
         if( !has_balance( bank, amount ) )
         {
            send_to_char( "You don't have that much to withdraw.\r\n", ch );
            return;
         }
         if( amount <= 0 )
         {
            send_to_char( "You can't withdraw 0 or less coins.\r\n", ch );
            return;
         }
      }

      if( !can_hold_gold( ch, amount ) )
      {
         send_to_char( "You can't hold that much gold.\r\n", ch );
         return;
      }

      increase_gold( ch, amount );
      decrease_balance( bank, amount );
      snprintf( buf, sizeof( buf ), "%s withdrawed %s gold.", ch->name, num_punct( amount ) );
      new_transaction( bank, buf );
      ch_printf( ch, "You have withdrawn %s gold ", num_punct( amount ) );
      ch_printf( ch, "and now have %s gold on account %s.\r\n", show_bank_balance( bank ), bank->account );
      if( bank->balance <= 0 )
      {
         ch_printf( ch, "As you get the last of the gold from account %s, it is closed.\r\n", bank->account );
         remove_bank( bank );
         free_bank( bank );
      }
      save_banks( );
      save_char_obj( ch );
      return;
   }

   /* Transfer amount */
   if( type == BANK_TRANSFER )
   {
      if( uall )
      {
         if( bank->balance > MGOLD )
         {
            send_to_char( "You can't transfer that much at once.\r\n", ch );
            return;
         }
         amount = (int)bank->balance;
      }
      else
      {
         if( amount <= 0 )
         {
            send_to_char( "You can't transfer 0 or less gold.\r\n", ch );
            return;
         }
         if( !has_balance( bank, amount ) )
         {
            send_to_char( "You don't have that much in that account.\r\n", ch );
            return;
         }
      }

      if( !( tbank = find_bank( taccount ) ) )
      {
         send_to_char( "Invalid account to transfer funds to.\r\n", ch );
         return;
      }

      if( !can_hold_balance( tbank, amount ) )
      {
         send_to_char( "That account can't hold that much gold.\r\n", ch );
         return;
      }

      snprintf( buf, sizeof( buf ), "%s transfered %s gold from %s.", ch->name, num_punct( amount ), bank->account );
      new_transaction( tbank, buf );
      snprintf( buf, sizeof( buf ), "%s transfered %s gold to %s.", ch->name, num_punct( amount ), tbank->account );
      new_transaction( bank, buf );

      decrease_balance( bank, amount );

      ch_printf( ch, "You have transfered %s gold from %s to %s.\r\n", num_punct( amount ), bank->account, tbank->account );

      increase_balance( ch, tbank, amount );

      if( bank->balance <= 0 )
      {
         remove_bank( bank );
         free_bank( bank );
         send_to_char( "As you transfer the last of the gold in the account, it is closed.\r\n", ch );
      }

      save_banks( );
      return;
   }

   send_to_char( "Ok, now exactly how did you get here???\r\n", ch );
}

void bank_Usage_display( CHAR_DATA *ch )
{
   send_to_char( "Usage: bank [<account>] delete/balance\r\n", ch );
   send_to_char( "Usage: bank [<account>] share <name>\r\n", ch );
   send_to_char( "Usage: bank [<account>] create/deposit/withdraw <amount>/all.\r\n", ch );
   send_to_char( "Usage: bank [<account>] transfer <amount> <account>\r\n", ch );
}

/* Find the first bank someone has access to */
char *get_first_bank( CHAR_DATA *ch )
{
   BANK_DATA *bank;

   for( bank = first_bank; bank; bank = bank->next )
   {
      if( !can_access_bank( bank, ch ) )
         continue;
      return bank->account;
   }

   return NULL;
}

CMDF( do_bank )
{
   CHAR_DATA *banker = NULL;
   BANK_DATA *bank;
   char arg[MIL], account[MIL], *nbank;
   int amount = 0, bamount = 0, count = 0;
   short type;
   bool uall = false;

   /* Show a character all the bank accounts they can access and the amount */
   if( ( !argument || argument[0] == '\0' ) )
   {
      for( bank = first_bank; bank; bank = bank->next )
      {
         if( !can_access_bank( bank, ch ) )
            continue;

         amount++;
         ch_printf( ch, "&[white]%15.15s &[yellow]%19s&D", bank->account, show_bank_balance( bank ) );
         if( ++count == 3 )
         {
            count = 0;
            send_to_char( "\r\n", ch );
         }
         else
            send_to_char( " ", ch );
      }
      if( count != 0 )
         send_to_char( "\r\n", ch );
      if( amount == 0 )
         send_to_char( "&[white]No accounts found to display.&D\r\n", ch );
      else
         ch_printf( ch, "&[lblue]%d &[white]accounts.&D\r\n", amount );     
      return;
   }

   if( !( banker = find_banker( ch ) ) )
   {
      send_to_char( "You aren't at a banker.\r\n", ch );
      return;
   }

   argument = one_argument( argument, account );
   if( account == NULL || account[0] == '\0' )
   {
      bank_Usage_display( ch );
      return;
   }

   if( !str_cmp( account, "delete" ) && str_cmp( argument, "delete" ) )
   {
      send_to_char( "Please specify the account you want to delete.\r\n", ch );
      return;
   }

   /* Just put arg as account */
   if( !str_cmp( account, "create" ) || !str_cmp( account, "balance" )
   || !str_cmp( account, "deposit" ) || !str_cmp( account, "withdraw" )
   || !str_cmp( account, "transfer" ) || !str_cmp( account, "share" ) )
   {
      snprintf( arg, sizeof( arg ), "%s", account );
      if( !( nbank = get_first_bank( ch ) ) )
         snprintf( account, sizeof( account ), "%s", ch->name );
      else
         snprintf( account, sizeof( account ), "%s", nbank );
   }
   else
      argument = one_argument( argument, arg );

   if( !str_cmp( arg, "create" ) )        type = BANK_CREATE;
   else if( !str_cmp( arg, "balance" ) )  type = BANK_BALANCE;
   else if( !str_cmp( arg, "deposit" ) )  type = BANK_DEPOSIT;
   else if( !str_cmp( arg, "withdraw" ) ) type = BANK_WITHDRAW;
   else if( !str_cmp( arg, "transfer" ) ) type = BANK_TRANSFER;
   else if( !str_cmp( arg, "delete" ) )   type = BANK_DELETE;
   else if( !str_cmp( arg, "share" ) )    type = BANK_SHARE;
   else
   {
      bank_Usage_display( ch );
      return;
   }

   if( type != BANK_SHARE && type != BANK_DELETE && type != BANK_BALANCE )
   {
      argument = one_argument( argument, arg );
      if( !str_cmp( arg, "all" ) )
         uall = true;
      else
      {
         if( !is_number( arg ) || !( amount = atoi( arg ) ) || amount <= 0 || amount > MGOLD )
         {
            ch_printf( ch, "You must specify a valid number from 1 - %s.\r\n", num_punct( MGOLD ) );
            return;
         }
      }
   }

   if( type != BANK_SHARE && type != BANK_DELETE && type != BANK_BALANCE && amount <= 0 && bamount <= 0 && !uall )
   {
      bank_Usage_display( ch );
      return;
   }

   handle_bank( ch, account, type, amount, argument, uall );
}

void remove_from_banks( const char *player )
{
   BANK_DATA *bank, *bank_next;
   SHARE_DATA *shared, *shared_next;
   bool resave = false;

   for( bank = first_bank; bank; bank = bank_next )
   {
      bank_next = bank->next;

      /* Remove all banks created by the player */
      if( !str_cmp( bank->created, player ) )
      {
         remove_bank( bank );
         free_bank( bank );
         resave = true;
         continue;
      }

      /* Remove player from any banks it has access to */
      for( shared = bank->first_share; shared; shared = shared_next )
      {
         shared_next = shared->next;

         if( !str_cmp( shared->name, player ) )
         {
            remove_shared( bank, shared );
            free_shared( shared );
            resave = true;
         }
      }
   }

   if( resave )
      save_banks( );
}

void rename_in_banks( const char *oplayer, const char *nplayer )
{
   BANK_DATA *bank, *bank_next;
   SHARE_DATA *shared, *shared_next;
   bool resave = false;

   for( bank = first_bank; bank; bank = bank_next )
   {
      bank_next = bank->next;

      if( !str_cmp( bank->created, oplayer ) )
      {
         STRSET( bank->created, nplayer );
         resave = true;
      }

      /* Rename player for any banks it has access to */
      for( shared = bank->first_share; shared; shared = shared_next )
      {
         shared_next = shared->next;

         if( !str_cmp( shared->name, oplayer ) )
         {
            STRSET( shared->name, nplayer );
            resave = true;
         }
      }
   }

   if( resave )
      save_banks( );
}