LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/classes/
LOP/color/
LOP/councils/
LOP/deity/
LOP/races/
LOP/src/specials/
/*****************************************************************************
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008 by: the LoP team.                                *
 *---------------------------------------------------------------------------*
 *                          Dynamic Channel Handler                          *
 *****************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "h/mud.h"

void add_phistory( int type, CHAR_DATA *ch, char *argument );
char *drunk_speech( const char *argument, CHAR_DATA *ch );

#define CHANNEL_DIR "channels/"
#define CHANNEL_FILE CHANNEL_DIR "channels.dat"

typedef struct chistory_data CHISTORY_DATA;
typedef struct channel_data CHANNEL_DATA;

CHANNEL_DATA *first_channel, *last_channel;

struct chistory_data
{
   CHISTORY_DATA *next, *prev;
   char *name;
   char *text;
   char *restrict;
   time_t chtime;
   int speaking;
};

struct channel_data
{
   CHANNEL_DATA *next, *prev;
   CHISTORY_DATA *first_chistory, *last_chistory;
   char *name;
   char *color;
   int type;
   int permission;
   int currhistory;
   int maxhistory;
};

int bug_report = 0;

bool check_is_ignoring( CHAR_DATA *ch, char *name )
{
   IGNORE_DATA *temp;

   if( is_npc( ch ) )
      return false;

   for( temp = ch->pcdata->first_ignored; temp; temp = temp->next )
   {
      if( nifty_is_name( temp->name, name ) )
         return true;
   }

   return false;
}

void save_chistory( CHANNEL_DATA *channel )
{
   CHISTORY_DATA *chistory;
   FILE *fp;
   char filename[MSL];

   if( !channel )
      return;
   
   sprintf( filename, "%s%s", CHANNEL_DIR, channel->name );

   if( !channel->first_chistory )
   {
      remove_file( filename );
      return;
   }

   if( !( fp = fopen( filename, "w" ) ) )
   {
      if( ++bug_report > 1 )
         return;
      bug( "%s: Can't open %s for writing.", __FUNCTION__, filename );
      perror( filename );
      return;
   }

   bug_report = 0;

   for( chistory = channel->first_chistory; chistory; chistory = chistory->next )
   {
      if( !chistory->text )
         continue;
      fprintf( fp, "%s", "#CHISTORY\n" );
      if( chistory->name )
         fprintf( fp, "Name     %s~\n", chistory->name );
      fprintf( fp, "Time     %ld\n", chistory->chtime );
      if( chistory->speaking != -1 )
         fprintf( fp, "Speaking %d\n", chistory->speaking );
      fprintf( fp, "Text     %s~\n", chistory->text );
      if( chistory->restrict )
         fprintf( fp, "Restrict %s~\n", chistory->restrict );
      fprintf( fp, "%s", "End\n\n" );
   }
   fprintf( fp, "%s", "#END\n" );
   fclose( fp );
   fp = NULL;
}

void save_all_chistorys( void )
{
   CHANNEL_DATA *channel;

   for( channel = first_channel; channel; channel = channel->next )
      save_chistory( channel );   
}

void save_channels( void )
{
   CHANNEL_DATA *channel;
   FILE *fp;

   if( !( fp = fopen( CHANNEL_FILE, "w" ) ) )
   {
      bug( "%s: Can't open %s for writing.", __FUNCTION__, CHANNEL_FILE );
      perror( CHANNEL_FILE );
      return;
   }
   for( channel = first_channel; channel; channel = channel->next )
   {
       fprintf( fp, "%s", "#CHANNEL\n" );
       fprintf( fp, "Name       %s~\n", channel->name );
       fprintf( fp, "Color      %s~\n", channel->color );
       if( channel->type >= 0 && channel->type < CHANNEL_MAX )
          fprintf( fp, "Type       %s~\n", channelflags[channel->type] );
       if( channel->permission >= 0 && channel->permission < PERM_MAX )
          fprintf( fp, "Permission %s~\n", perms_flag[channel->permission] );
       fprintf( fp, "MaxHistory %d\n", channel->maxhistory );
       fprintf( fp, "%s", "End\n\n" );
   }
   fprintf( fp, "%s", "#END\n" );
   fclose( fp );
   fp = NULL;
}

void free_chistory( CHISTORY_DATA *chistory )
{
   if( !chistory )
      return;
   STRFREE( chistory->name );
   STRFREE( chistory->text );
   STRFREE( chistory->restrict );
   DISPOSE( chistory );
}

void free_channel( CHANNEL_DATA *channel )
{
   CHISTORY_DATA *chistory, *chistory_next;

   if( !channel )
      return;
   STRFREE( channel->name );
   STRFREE( channel->color );
   for( chistory = channel->first_chistory; chistory; chistory = chistory_next )
   {
      chistory_next = chistory->next;
      UNLINK( chistory, channel->first_chistory, channel->last_chistory, next, prev );
      free_chistory( chistory );
   }
   DISPOSE( channel );
}

CHANNEL_DATA *get_channel( char *argument )
{
   CHANNEL_DATA *channel;

   for( channel = first_channel; channel; channel = channel->next )
   {
      if( !str_cmp( channel->name, argument ) )
      {
         return channel;
      }
   }
   return NULL;
}

void show_chistory( CHAR_DATA *ch, CHANNEL_DATA *channel, bool showall )
{
   CHISTORY_DATA *chistory;
   bool found = false;

   if( !ch || !channel )
      return;
   for( chistory = channel->first_chistory; chistory; chistory = chistory->next )
   {
      if( !showall && chistory->restrict && ch->pcdata )
      {
         if( channel->type == CHANNEL_RACETALK
         && ( !race_table[ch->race] || str_cmp( chistory->restrict, race_table[ch->race]->name ) ) )
            continue;
         if( channel->type == CHANNEL_CLASS
         && ( !class_table[ch->Class] || str_cmp( chistory->restrict, class_table[ch->Class]->name ) ) )
            continue;
         if( channel->type == CHANNEL_CLAN
         && ( !ch->pcdata || !ch->pcdata->clan || str_cmp( chistory->restrict, ch->pcdata->clan->name ) ) )
            continue;
         if( channel->type == CHANNEL_NATION
         && ( !ch->pcdata || !ch->pcdata->nation || str_cmp( chistory->restrict, ch->pcdata->nation->name ) ) )
            continue;
         if( channel->type == CHANNEL_COUNCIL
         && ( !ch->pcdata || !ch->pcdata->council || str_cmp( chistory->restrict, ch->pcdata->council->name ) ) )
            continue;
         if( channel->type == CHANNEL_YELL
         && ( !ch->in_room || !ch->in_room->area || str_cmp( chistory->restrict, ch->in_room->area->name ) ) )
           continue;
         if( channel->type == CHANNEL_LOG && get_trust( ch ) < atoi( chistory->restrict ) )
            continue;
      }

      if( !showall && chistory->name && check_is_ignoring( ch, chistory->name ) )
         continue;

      found = true;

      ch_printf( ch, "%s[%s]", channel->color ? channel->color : "&[plain]", distime( chistory->chtime ) );
      if( chistory->name )
      {
         if( !str_cmp( ch->name, chistory->name ) )
            ch_printf( ch, " You %s", channel->name );
         else
            ch_printf( ch, " %s %ss", chistory->name, channel->name );
      }

      send_to_char( " ' ", ch );

      if( chistory->speaking == -1 )
         send_to_char( chistory->text, ch );
      else
      {
         int speakswell = knows_language( ch, chistory->speaking );
         char *sbuf = chistory->text;

         if( speakswell < 85 )
            sbuf = translate( speakswell, chistory->text, lang_names[chistory->speaking] );
         send_to_char( sbuf, ch );
      }

      ch_printf( ch, " &D%s'\r\n", channel->color ? channel->color : "&[plain]" );
   }
   if( !found )
      ch_printf( ch, "Nothing has been said on %s.\r\n", channel->name );
}

CMDF( do_setchannel )
{
   CHANNEL_DATA *channel;
   char arg[MIL], arg2[MIL];
   int col = 0;

   if( !argument || argument[0] == '\0' )
   {
      send_to_char( "Usage: setchannel <channel>\r\n", ch );
      send_to_char( "Usage: setchannel <channel> create/delete/clearhistory\r\n", ch );
      send_to_char( "Usage: setchannel <channel> name/color/type/perm/maxhistory <setting>\r\n", ch );
      for( channel = first_channel; channel; channel = channel->next )
      {
         ch_printf( ch, "%s[%3d]", channel->color ? channel->color : "&[plain]", channel->currhistory );
         ch_printf( ch, "%-20.20s", channel->name );
         if( ++col == 3 )
         {
            col = 0;
            send_to_char( "\r\n", ch );
         }
      }
      if( col != 0 )
         send_to_char( "\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg );
   argument = one_argument( argument, arg2 );
   channel = get_channel( arg );
   if( !arg2 || arg2[0] == '\0' )
   {
      if( !channel )
      {
         ch_printf( ch, "No channel by the name of %s.\r\n", arg );
         return;
      }
      ch_printf( ch, "&CName:       &W%s\r\n", channel->name );
      ch_printf( ch, "&CColor:      %sColored&D\r\n", channel->color );
      ch_printf( ch, "&CType:       &W%s\r\n", channelflags[channel->type] );
      ch_printf( ch, "&CPerm:       &W%s\r\n", perms_flag[channel->permission] );
      ch_printf( ch, "&CHistory:    &W%d\r\n", channel->currhistory );
      ch_printf( ch, "&CMaxhistory: &W%d&D\r\n", channel->maxhistory );
      send_to_char( "Full channel history:\r\n", ch );
      show_chistory( ch, channel, true );
      return;
   }

   if( !str_cmp( arg2, "create" ) )
   {
      if( channel )
      {
         ch_printf( ch, "There is already a channel named %s.\r\n", channel->name );
         return;
      }
      CREATE( channel, CHANNEL_DATA, 1 );
      channel->name = STRALLOC( arg );
      channel->color = STRALLOC( "&[plain]" );
      channel->type = CHANNEL_GLOBAL;
      channel->permission = PERM_ALL;
      channel->maxhistory = 20;
      channel->currhistory = 0;
      channel->first_chistory = channel->last_chistory = NULL;
      LINK( channel, first_channel, last_channel, next, prev );
      save_channels( );
      ch_printf( ch, "%s channel created.\r\n", channel->name );
      return;
   }

   if( !channel )
   {
      ch_printf( ch, "No channel by the name of %s.\r\n", arg );
      return;
   }
   if( !str_cmp( arg2, "delete" ) )
   {
      UNLINK( channel, first_channel, last_channel, next, prev );
      free_channel( channel );
      save_channels( );
      ch_printf( ch, "%s channel has been deleted.\r\n", arg );
      return;
   }
   if( !str_cmp( arg2, "clearhistory" ) )
   {
      char filename[MSL];
      CHISTORY_DATA *chistory, *chistory_next;

      for( chistory = channel->first_chistory; chistory; chistory = chistory_next )
      {
         chistory_next = chistory->next;
         UNLINK( chistory, channel->first_chistory, channel->last_chistory, next, prev );
         free_chistory( chistory );
      }
      sprintf( filename, "%s%s", CHANNEL_DIR, channel->name );
      remove_file( filename );
      channel->currhistory = 0;
      ch_printf( ch, "%s's history has been cleared.\r\n", channel->name );
      return;
   }
   if( !str_cmp( arg2, "color" ) )
   {
      STRSET( channel->color, argument );
      save_channels( );
      ch_printf( ch, "%s's color has been set.\r\n", channel->name );
      return;
   }
   if( !str_cmp( arg2, "type" ) )
   {
      int value = get_flag( argument, channelflags, CHANNEL_MAX );

      if( value < 0 || value >= CHANNEL_MAX )
      {
         send_to_char( "Invalid channel type.\r\n", ch );
         return;
      }
      channel->type = value;
      save_channels( );
      ch_printf( ch, "%s type has been set to %s.\r\n", channel->name, channelflags[channel->type] );
      return;
   }
   if( !str_cmp( arg2, "perm" ) )
   {
      int value = get_flag( argument, perms_flag, PERM_MAX );
      if( value < 0 || value >= PERM_MAX )
      {
         send_to_char( "Invalid permission.\r\n", ch );
         return;
      }
      channel->permission = value;
      save_channels( );
      ch_printf( ch, "%s permission has been set to %s.\r\n", channel->name, perms_flag[channel->permission] );
      return;
   }
   if( !str_cmp( arg2, "maxhistory" ) )
   {
      channel->maxhistory = atoi( argument );
      channel->maxhistory = URANGE( 0, channel->maxhistory, 100 );
      save_channels( );
      ch_printf( ch, "%s maxhistory has been set to %d.\r\n", channel->name, channel->maxhistory );
      return;
   }
   if( !str_cmp( arg2, "name" ) )
   {
      STRSET( channel->name, argument );
      save_channels( );
      ch_printf( ch, "%s channel has been changed to %s.\r\n", arg, channel->name );
      return;
   }
   do_setchannel( ch, "" );
}

void fread_chistory( CHANNEL_DATA *channel, FILE *fp )
{
   const char *word;
   bool fMatch;
   CHISTORY_DATA *chistory;

   CREATE( chistory, CHISTORY_DATA, 1 );
   chistory->name = NULL;
   chistory->text = NULL;
   chistory->restrict = NULL;
   chistory->speaking = -1;

   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" ) )
            {
               LINK( chistory, channel->first_chistory, channel->last_chistory, next, prev );
               channel->currhistory++;
               return;
            }
            break;

         case 'N':
            KEY( "Name", chistory->name, fread_string( fp ) );
            break;

         case 'R':
            KEY( "Restrict", chistory->restrict, fread_string( fp ) );
            break;

         case 'S':
            KEY( "Speaking", chistory->speaking, fread_number( fp ) );
            break;

         case 'T':
            KEY( "Text", chistory->text, fread_string( fp ) );
            KEY( "Time", chistory->chtime, fread_time( fp ) );
            break;
      }

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

void load_chistorys( CHANNEL_DATA *channel )
{
   FILE *fp;
   char filename[MSL];

   if( !channel || !channel->name )
      return;

   sprintf( filename, "%s%s", CHANNEL_DIR, channel->name );
   if( !( fp = fopen( filename, "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, "CHISTORY" ) )
      {
         fread_chistory( channel, 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;
}

void fread_channel( FILE *fp )
{
   const char *word;
   char *infoflags, flag[MIL];
   bool fMatch;
   CHANNEL_DATA *channel;
   int value;

   CREATE( channel, CHANNEL_DATA, 1 );
   channel->name = NULL;
   channel->type = CHANNEL_GLOBAL;
   channel->permission = PERM_ALL;
   channel->maxhistory = 0;
   channel->currhistory = 0;
   channel->first_chistory = channel->last_chistory = NULL;

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

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

         case 'C':
            KEY( "Color", channel->color, fread_string( fp ) );
            break;

         case 'E':
            if( !str_cmp( word, "End" ) )
            {
               if( !channel->color )
                  channel->color = STRALLOC( "&[plain]" );
               LINK( channel, first_channel, last_channel, next, prev );
               load_chistorys( channel );
               return;
            }
            break;

         case 'M':
            KEY( "MaxHistory", channel->maxhistory, fread_number( fp ) );
            break;

         case 'N':
            KEY( "Name", channel->name, fread_string( fp ) );
            break;

         case 'P':
            SKEY( "Permission", channel->permission, fp, perms_flag, PERM_MAX );
            break;

         case 'T':
            SKEY( "Type", channel->type, fp, channelflags, CHANNEL_MAX );
            break;
      }

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

void load_channels( void )
{
   FILE *fp;

   first_channel = last_channel = NULL;
   if( !( fp = fopen( CHANNEL_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, "CHANNEL" ) )
      {
         fread_channel( 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;
}

void free_all_channels( void )
{
   CHANNEL_DATA *channel, *channel_next;

   for( channel = first_channel; channel; channel = channel_next )
   {
      channel_next = channel->next;
      UNLINK( channel, first_channel, last_channel, next, prev );
      free_channel( channel );
   }
}

void add_to_history( CHANNEL_DATA *channel, CHAR_DATA *ch, int trust, int speaking, char *argument )
{
   CHISTORY_DATA *chistory, *chistory_next, *chistory_remove = NULL;
   int y = 0;

   if( !channel || !argument || argument[0] == '\0' )
      return;
   if( !channel->first_chistory && channel->maxhistory <= 0 )
      return;
   for( chistory = channel->first_chistory; chistory; chistory = chistory_next )
   {
      chistory_next = chistory->next;
      if( chistory->restrict && ch && ch->pcdata )
      {
         if( channel->type == CHANNEL_RACETALK
         && ( !race_table[ch->race] || str_cmp( chistory->restrict, race_table[ch->race]->name ) ) )
            continue;
         if( channel->type == CHANNEL_CLASS
         && ( !class_table[ch->Class] || str_cmp( chistory->restrict, class_table[ch->Class]->name ) ) )
            continue;
         if( channel->type == CHANNEL_CLAN
         && ( !ch->pcdata || !ch->pcdata->clan || str_cmp( chistory->restrict, ch->pcdata->clan->name ) ) )
            continue;
         if( channel->type == CHANNEL_NATION
         && ( !ch->pcdata || !ch->pcdata->nation || str_cmp( chistory->restrict, ch->pcdata->nation->name ) ) )
            continue;
         if( channel->type == CHANNEL_COUNCIL
         && ( !ch->pcdata || !ch->pcdata->council || str_cmp( chistory->restrict, ch->pcdata->council->name ) ) )
            continue;
         if( channel->type == CHANNEL_YELL
         && ( !ch->in_room || !ch->in_room->area || str_cmp( chistory->restrict, ch->in_room->area->name ) ) )
            continue;
      }
      if( !chistory_remove )
         chistory_remove = chistory;
      if( ++y >= channel->maxhistory )
      {
         UNLINK( chistory_remove, channel->first_chistory, channel->last_chistory, next, prev );
         free_chistory( chistory_remove );
         channel->currhistory--;
         chistory_remove = NULL;
         y--;
      }
   }
   if( channel->maxhistory <= 0 )
      return;
   chistory = NULL;
   CREATE( chistory, CHISTORY_DATA, 1 );
   chistory->speaking = speaking;
   smash_tilde( argument );
   chistory->text = STRALLOC( argument );
   chistory->restrict = NULL;
   if( ch )
   {
      chistory->name = STRALLOC( ch->name );
      if( channel->type == CHANNEL_RACETALK && race_table[ch->race] )
         chistory->restrict = STRALLOC( race_table[ch->race]->name );
      if( channel->type == CHANNEL_CLASS && class_table[ch->Class] )
         chistory->restrict = STRALLOC( class_table[ch->Class]->name );
      if( channel->type == CHANNEL_CLAN && ch->pcdata && ch->pcdata->clan && ch->pcdata->clan->name )
         chistory->restrict = STRALLOC( ch->pcdata->clan->name );
      if( channel->type == CHANNEL_NATION && ch->pcdata && ch->pcdata->nation && ch->pcdata->nation->name )
         chistory->restrict = STRALLOC( ch->pcdata->nation->name );
      if( channel->type == CHANNEL_COUNCIL && ch->pcdata && ch->pcdata->council && ch->pcdata->council->name )
         chistory->restrict = STRALLOC( ch->pcdata->council->name );
      if( channel->type == CHANNEL_YELL && ch->in_room && ch->in_room->area && ch->in_room->area->name )
         chistory->restrict = STRALLOC( ch->in_room->area->name );
      if( channel->type == CHANNEL_LOG )
      {
         char buf[MSL];

         snprintf( buf, sizeof( buf ), "%d", get_trust( ch ) );
         chistory->restrict = STRALLOC( buf );
      }
   }
   else if( channel->type == CHANNEL_LOG && trust )
   {
      char buf[MSL];

      snprintf( buf, sizeof( buf ), "%d", trust );
      chistory->restrict = STRALLOC( buf );
   }

   chistory->chtime = current_time;
   LINK( chistory, channel->first_chistory, channel->last_chistory, next, prev );
   channel->currhistory++;
   save_chistory( channel );
}

bool can_use_channel( CHAR_DATA *ch, CHANNEL_DATA *channel )
{
   if( !channel )
      return false;
   if( !ch )
      return true;
   if( get_trust( ch ) < channel->permission )
      return false;
   if( channel->type == CHANNEL_RACETALK && !race_table[ch->race] )
      return false;
   if( channel->type == CHANNEL_CLASS && !class_table[ch->Class] )
      return false;
   if( channel->type == CHANNEL_CLAN && ( !ch->pcdata || !ch->pcdata->clan ) )
      return false;
   if( channel->type == CHANNEL_NATION && ( !ch->pcdata || !ch->pcdata->nation ) )
      return false;
   if( channel->type == CHANNEL_COUNCIL && ( !ch->pcdata || !ch->pcdata->council ) )
      return false;
   if( channel->type == CHANNEL_YELL && ( !ch->in_room || !ch->in_room->area ) )
      return false;
   return true;
}

CMDF( do_channels )
{
   CHANNEL_DATA *channel;
   int col = 0;

   if( !ch )
      return;
   for( channel = first_channel; channel; channel = channel->next )
   {
      if( !can_use_channel( ch, channel ) )
         continue;
      ch_printf( ch, "%s%20.20s", channel->color ? channel->color : "&[plain]", channel->name );
      if( ++col == 4 )
      {
         col = 0;
         send_to_char( "\r\n", ch );
      }
   }
   if( col != 0 )
      send_to_char( "\r\n", ch );
}

bool is_listening( CHAR_DATA *ch, CHANNEL_DATA *channel )
{
   char *listening, chan[MIL];

   if( !ch || !channel )
      return false;
   listening = ch->pcdata->channels;
   while( listening && listening[0] != '\0' )
   {
      listening = one_argument( listening, chan );
      if( !str_cmp( channel->name, chan ) )
         return true;
   }
   return false;
}

bool handle_channels( CHAR_DATA *ch, CHANNEL_DATA *channel, int trust, char *argument )
{
   DESCRIPTOR_DATA *d, *d_next;
   PER_HISTORY *phistory;
   char buf[MSL], hbuf[MSL], thbuf[MSL];
   int speaking = -1, lang;

   if( !channel )
      return false;

   if( !ch && ( !argument || argument[0] == '\0' ) )
      return false;

   if( ch && !can_use_channel( ch, channel ) )
      return false;

   hbuf[0] = '\0';
   if( ch )
   {
      if( !argument || argument[0] == '\0' || channel->type == CHANNEL_LOG )
      {
         if( !is_npc( ch ) && channel->type == CHANNEL_YELL )
         {
            if( !ch->pcdata->first_yell )
               send_to_char( "You haven't heard anyone yell.\r\n", ch );
            else
            {
               for( phistory = ch->pcdata->first_yell; phistory; phistory = phistory->next )
                  send_to_char( phistory->text, ch );
            }
            return true;
         }
         show_chistory( ch, channel, false );
         return true;
      }
     
      for( lang = 0; lang_array[lang] != LANG_UNKNOWN; lang++ )
      {
         if( xIS_SET( ch->speaking, lang_array[lang] ) )
         {
            speaking = lang;
            break;
         }
      }

      ch_printf( ch, "%sYou %s ' %s &D%s'\r\n", channel->color ? channel->color : "&[plain]",
         channel->name, argument, channel->color ? channel->color : "&[plain]" );
      snprintf( buf, sizeof( buf ), "%s%s %ss ' %s &D%s'\r\n", channel->color ? channel->color : "&[plain]",
         ch->name, channel->name, argument, channel->color ? channel->color : "&[plain]" );
      if( channel->type == CHANNEL_YELL )
         snprintf( hbuf, sizeof( hbuf ), "%s%s yelled ' %s &D%s'\r\n", channel->color ? channel->color : "&[plain]",
            capitalize( is_npc( ch ) ? ch->short_descr : ch->name ), argument, channel->color ? channel->color : "&[plain]" );
   }
   else
   {
      snprintf( buf, sizeof( buf ), "%s%s: %s\r\n", channel->color, channel->name, argument );
      if( channel->type == CHANNEL_YELL )
         snprintf( hbuf, sizeof( hbuf ), "%s%s yelled ' %s &D%s'\r\n", channel->color ? channel->color : "&[plain]",
            ch ? ( capitalize( is_npc( ch ) ? ch->short_descr : ch->name ) ) : "Unknown", argument, channel->color ? channel->color : "&[plain]" );
   }
   add_to_history( channel, ch, trust, speaking, argument );

   for( d = first_descriptor; d; d = d_next )
   {
      d_next = d->next;
      if( !d->character )
          continue;
      if( ch && d->character == ch )
         continue;
      if( !can_use_channel( d->character, channel ) )
         continue;
      if( ch && ch->pcdata && d->character && d->character->pcdata )
      {
         if( channel->type == CHANNEL_RACETALK && ch->race != d->character->race )
            continue;
         if( channel->type == CHANNEL_CLASS && ch->Class != d->character->Class )
            continue;
         if( channel->type == CHANNEL_CLAN && ch->pcdata->clan != d->character->pcdata->clan )
            continue;
         if( channel->type == CHANNEL_NATION && ch->pcdata->nation != d->character->pcdata->nation )
            continue;
         if( channel->type == CHANNEL_COUNCIL && ch->pcdata->council != d->character->pcdata->council )
            continue;
         if( channel->type == CHANNEL_YELL
         && ( !ch->in_room || !d->character->in_room || ch->in_room->area != d->character->in_room->area ) )
            continue;
      }

      if( channel->type == CHANNEL_LOG && get_trust( d->character ) < ( ch ? get_trust( ch ) : trust ) )
         continue;

      if( !is_listening( d->character, channel ) )
         continue;

      /* Check to see if character is ignoring speaker */
      if( ch && d->character && is_ignoring( d->character, ch ) )
      {
         /* continue unless speaker is an immortal */
         if( !is_immortal( ch ) || get_trust( d->character ) > get_trust( ch ) )
            continue;
         else
         {
            set_char_color( AT_IGNORE, d->character );
            ch_printf( d->character, "You attempt to ignore %s, but are unable to do so.\r\n", ch->name );
         }
      }

      if( speaking == -1 )
      {
         send_to_char( buf, d->character );
         snprintf( thbuf, sizeof( thbuf ), "%s", hbuf );
      }
      else
      {
         int speakswell = knows_language( d->character, speaking );
         char *sbuf = argument;

         if( speakswell < 85 )
            sbuf = translate( speakswell, argument, lang_names[speaking] );

         if( ch )
         {
            sbuf = drunk_speech( sbuf, ch );

            snprintf( buf, sizeof( buf ), "%s%s %ss ' %s &D%s'\r\n", channel->color ? channel->color : "&[plain]",
               ch->name, channel->name, sbuf, channel->color ? channel->color : "&[plain]" );
         }
         else
            snprintf( buf, sizeof( buf ), "%s%s: %s\r\n", channel->color, channel->name, sbuf );

         send_to_char( buf, d->character );
         if( channel->type == CHANNEL_YELL )
            snprintf( thbuf, sizeof( thbuf ), "%s%s yelled ' %s &D%s'\r\n", channel->color ? channel->color : "&[plain]",
               capitalize( is_npc( ch ) ? ch->short_descr : ch->name ), sbuf, channel->color ? channel->color : "&[plain]" );
      }

      if( channel->type == CHANNEL_YELL )
      {
         if( !is_npc( d->character ) )
            add_phistory( 2, d->character, thbuf );
      }
   }
   return true;
}

bool check_channel( CHAR_DATA *ch, char *command, char *argument )
{
   CHANNEL_DATA *channel;

   for( channel = first_channel; channel; channel = channel->next )
   {
      if( !channel->name )
         continue;
      if( ch && !can_use_channel( ch, channel ) )
         continue;
      if( !str_prefix( command, channel->name ) )
      {
         handle_channels( ch, channel, get_trust( ch ), argument );
         return true;
      }
   }
   return false;
}

/* Writes a string to the log, extended version - Thoric */
void log_string_plus( const char *str, short log_type, int level )
{
   CHANNEL_DATA *channel;
   struct timeval now_time;

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

   fprintf( stderr, "%s :: %s\n", distime( current_time ), str );
   switch( log_type )
   {
      default:
         if( ( channel = get_channel( "log" ) ) )
            handle_channels( NULL, channel, level, ( char * )str );
         break;

      case LOG_BUG:
         if( ( channel = get_channel( "bug" ) ) )
            handle_channels( NULL, channel, level, ( char * )str );
         break;

      case LOG_BUILD:
         if( ( channel = get_channel( "build" ) ) )
            handle_channels( NULL, channel, level, ( char * )str );
         break;

      case LOG_COMM:
         if( ( channel = get_channel( "comm" ) ) )
            handle_channels( NULL, channel, level, ( char * )str );
         break;

      case LOG_WARN:
         if( ( channel = get_channel( "warn" ) ) )
            handle_channels( NULL, channel, level, ( char * )str );
         break;

      case LOG_ALL:
         break;
   }
}

void log_printf_plus( short log_type, int level, const char *fmt, ... )
{
   char buf[MSL * 2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, sizeof( buf ), fmt, args );
   va_end( args );

   log_string_plus( buf, log_type, level );
}

void log_printf( const char *fmt, ... )
{
   char buf[MSL * 2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, sizeof( buf ), fmt, args );
   va_end( args );

   log_string_plus( buf, LOG_NORMAL, PERM_LOG );
}

void to_channel( char *argument, char *channel, int level )
{
   CHANNEL_DATA *chandata;

   if( argument[0] == '\0' )
      return;

   if( ( chandata = get_channel( channel ) ) )
      handle_channels( NULL, chandata, level, argument );
}

void to_channel_printf( char *channel, int level, char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, MAX_STRING_LENGTH * 2, fmt, args );
   va_end( args );

   to_channel( buf, channel, level );
}

CMDF( do_listen )
{
   CHANNEL_DATA *c;
   char arg[MIL];

   if( !argument || argument[0] == '\0' )
   {
      send_to_char( "&cCurrently tuned into:\r\n", ch );
      if( ch->pcdata->channels )
      {
         char *listening = ch->pcdata->channels;
         int col = 0;

         while( listening && listening[0] != '\0' )
         {
            listening = one_argument( listening, arg );
            ch_printf( ch, "&W%20.20s", arg );
            if( ++col == 4 )
            {
               col = 0;
               send_to_char( "\r\n", ch );
            }
         }
         if( col != 0 )
            send_to_char( "\r\n", ch );
      }
      else
         send_to_char( "&WNone\r\n", ch );

      {
         char chan_new[MSL];
         char tmp_chan[MSL];
         int col = 0;

         chan_new[0] = '\0';
         for( c = first_channel; c; c = c->next )
         {
            if( get_trust( ch ) >= c->permission )
            {
               /* Listening to this channel? */
               if( is_listening( ch, c ) )
                  continue;

               snprintf( tmp_chan, sizeof( tmp_chan ), "&R%20.20s", c->name );
               mudstrlcat( chan_new, tmp_chan, sizeof( chan_new ) );
               if( ++col == 4 )
               {
                  col = 0;
                  mudstrlcat( chan_new, "\r\n", sizeof( chan_new ) );
               }
            }
         }
         if( chan_new && chan_new[0] != '\0' )
         {
            if( col != 0 )
               mudstrlcat( chan_new, "\r\n", sizeof( chan_new ) );
            send_to_char( "&cCurrently not listening to:\r\n", ch );
            send_to_char( chan_new, ch );
         }
      }
      return;
   }

   if( !str_cmp( argument, "all" ) )
   {
      char chan_new[MSL];

      chan_new[0] = '\0';
      for( c = first_channel; c; c = c->next )
      {
         if( get_trust( ch ) >= c->permission )
         {
            if( chan_new && chan_new[0] != '\0' )
               mudstrlcat( chan_new, " ", sizeof( chan_new ) );
            mudstrlcat( chan_new, c->name, sizeof( chan_new ) );
         }
      }
      if( !chan_new || chan_new[0] == '\0' )
         send_to_char( "&YNo channels for you to listen to.\r\n", ch );
      else
      {
         STRSET( ch->pcdata->channels, chan_new );
         send_to_char( "&YYou are now listening to all available channels.\r\n", ch );
      }
      return;
   }

   if( !str_cmp( argument, "none" ) )
   {
      STRFREE( ch->pcdata->channels );
      send_to_char( "&YYou no longer listen to any available channels.\r\n", ch );
      return;
   }

   while( argument && argument[0] != '\0' )
   {
      char *chan_buf, chan_tmp[MIL], chan_new[MSL];
      bool cremoved = false;

      argument = one_argument( argument, arg );

      if( !( c = get_channel( arg ) ) || get_trust( ch ) < c->permission )
      {
         ch_printf( ch, "No channel named %s.\r\n", arg );
         continue;;
      }

      chan_buf = ch->pcdata->channels;
      mudstrlcpy( chan_new, "", sizeof( chan_new ) );

      while( chan_buf && chan_buf[0] != '\0' )
      {
         chan_buf = one_argument( chan_buf, chan_tmp );
         if( !str_cmp( chan_tmp, c->name ) )
            cremoved = true;
         else
         {
            if( chan_new && chan_new[0] != '\0' )
               mudstrlcat( chan_new, " ", sizeof( chan_new ) );
            mudstrlcat( chan_new, chan_tmp, sizeof( chan_new ) );
         }
      }

      if( cremoved )
         ch_printf( ch, "You are no longer listening to %s.\r\n", c->name );
      else
      {
         if( chan_new && chan_new[0] != '\0' )
            mudstrlcat( chan_new, " ", sizeof( chan_new ) );
         mudstrlcat( chan_new, c->name, sizeof( chan_new ) );
         ch_printf( ch, "You are now listening to %s.\r\n", c->name );
      }

      STRSET( ch->pcdata->channels, chan_new );
   }
}