LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/classes/
LOP/color/
LOP/councils/
LOP/deity/
LOP/races/
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, 2007, 2008 by: the LoP team.                                *
 *****************************************************************************/

#include <stdio.h>
#include <string.h>
#include "h/mud.h"

CMDTYPE *find_command( char *command )
{
   CMDTYPE *cmd;
   int hash;

   if( !command || command[0] == '\0' )
      return NULL;

   hash = LOWER( command[0] ) % 126;

   for( cmd = command_hash[hash]; cmd; cmd = cmd->next )
      if( !str_prefix( command, cmd->name ) )
         return cmd;

   return NULL;
}

/* Check for the exact command used in creation/renaming */
CMDTYPE *find_exact_command( char *command )
{
   CMDTYPE *cmd;
   int hash;

   if( !command || command[0] == '\0' )
      return NULL;

   hash = LOWER( command[0] ) % 126;

   for( cmd = command_hash[hash]; cmd; cmd = cmd->next )
      if( !str_cmp( command, cmd->name ) )
         return cmd;

   return NULL;
}

void fread_command( FILE *fp )
{
   const char *word;
   bool fMatch;
   CMDTYPE *command;
   char *infoflags, flag[MSL];
   int value;

   CREATE( command, CMDTYPE, 1 );
   command->lag_count = 0; /* can't have caused lag yet... FB */
   xCLEAR_BITS( command->flags );

   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( "Code", command->fun_name, STRALLOC( fread_word( fp ) ) );
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
	    {
		if( !command->name )
		{
		   bug( "%s: Name not found", __FUNCTION__ );
		   free_command( command );
		   return;
		}

		if( !command->fun_name )
		{
		   bug( "%s: No function name supplied for %s", __FUNCTION__, command->name );
		   free_command( command );
		   return;
		}

		/*
	 	 * Mods by Trax
		 * Fread in code into char* and try linkage here then
		 * deal in the "usual" way I suppose..
		 */
	        command->do_fun = skill_function( command->fun_name );
		if( command->do_fun == skill_notfound )
		{
		   bug( "%s: Function %s not found for %s", __FUNCTION__, command->fun_name, command->name );
		   free_command( command );
		   return;
		}
		add_command( command );
		return;
	    }
	    break;

         case 'F':
            WEXTKEY( "Flags", command->flags, fp, cmd_flags, CMD_MAX );
            break;

         case 'L':
            KEY( "Log", command->log, fread_number( fp ) );
            break;

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

         case 'P':
            SKEY( "Position", command->position, fp, pos_names, POS_MAX );
            SKEY( "Perm", command->perm, fp, perms_flag, PERM_MAX );
            break;
      }

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

void load_commands( void )
{
   FILE *fp;

   if( !( fp = fopen( COMMAND_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, (%c) found instead.", __FUNCTION__, letter );
         break;
      }

      word = fread_word( fp );
      if( !str_cmp( word, "COMMAND" ) )
      {
         fread_command( fp );
         continue;
      }
      else if( !str_cmp( word, "END" ) )
         break;
      else
      {
         bug( "%s: bad section (%s).", __FUNCTION__, word );
         continue;
      }
   }
   fclose( fp );
   fp = NULL;
}

void save_commands( void )
{
   FILE *fp;
   CMDTYPE *command;
   int x;
   bool found = false;

   if( !( fp = fopen( COMMAND_FILE, "w" ) ) )
   {
      bug( "%s: Can't open %s for writing", __FUNCTION__, COMMAND_FILE );
      perror( COMMAND_FILE );
      return;
   }

   for( x = 0; x < 126; x++ )
   {
      for( command = command_hash[x]; command; command = command->next )
      {
         if( !command->name || command->name[0] == '\0' )
         {
            bug( "%s: blank command in hash bucket %d", __FUNCTION__, x );
            continue;
         }
         found = true;
         fprintf( fp, "#COMMAND\n" );
         fprintf( fp, "Name        %s~\n", command->name );
         fprintf( fp, "Code        %s\n", command->fun_name ? command->fun_name : "" );
         fprintf( fp, "Position    %s~\n", pos_names[command->position] );
         fprintf( fp, "Perm        %s~\n", perms_flag[command->perm] );
         fprintf( fp, "Log         %d\n", command->log );
         if( !xIS_EMPTY( command->flags ) )
            fprintf( fp, "Flags       %s~\n", ext_flag_string( &command->flags, cmd_flags ) );
         fprintf( fp, "End\n\n" );
      }
   }
   fprintf( fp, "#END\n" );
   fclose( fp );
   fp = NULL;
   if( !found )
      remove_file( COMMAND_FILE );
}

void free_command( CMDTYPE *command )
{
   STRFREE( command->name );
   STRFREE( command->fun_name );
   DISPOSE( command );
}

void free_commands( void )
{
   CMDTYPE *command, *cmd_next;
   int hash;

   for( hash = 0; hash < 126; hash++ )
   {
      for( command = command_hash[hash]; command; command = cmd_next )
      {
         cmd_next = command->next;
         command->next = NULL;
         command->do_fun = NULL;
         free_command( command );
      }
   }
}

void unlink_command( CMDTYPE *command )
{
   CMDTYPE *tmp, *tmp_next;
   int hash;

   if( !command )
   {
      bug( "%s: NULL command", __FUNCTION__ );
      return;
   }

   if( !command->name || command->name[0] == '\0' )
   {
      bug( "%s: command with invalid name", __FUNCTION__ );
      return;
   }

   hash = command->name[0] % 126;

   if( command == ( tmp = command_hash[hash] ) )
   {
      command_hash[hash] = tmp->next;
      return;
   }
   for( ; tmp; tmp = tmp_next )
   {
      tmp_next = tmp->next;
      if( command == tmp_next )
      {
         tmp->next = tmp_next->next;
         return;
      }
   }
}

void add_command( CMDTYPE *command )
{
   int hash, x;
   CMDTYPE *tmp, *prev;

   if( !command )
   {
      bug( "%s: NULL command", __FUNCTION__ );
      return;
   }

   if( !command->name )
   {
      bug( "%s: NULL command->name", __FUNCTION__ );
      return;
   }

   if( !command->do_fun )
   {
      bug( "%s: NULL command->do_fun", __FUNCTION__ );
      return;
   }

   for( x = 0; command->name[x] != '\0'; x++ )
      command->name[x] = LOWER( command->name[x] );

   hash = command->name[0] % 126;

   if( !( prev = tmp = command_hash[hash] ) )
   {
      command->next = command_hash[hash];
      command_hash[hash] = command;
      return;
   }

   for( ; tmp; tmp = tmp->next )
   {
      if( !tmp->next )
      {
         tmp->next = command;
         command->next = NULL;
      }
   }
}

void do_cedit( CHAR_DATA *ch, char *argument )
{
   CMDTYPE *command;
   char arg1[MIL], arg2[MIL];

   set_char_color( AT_IMMORT, ch );

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );
   if( !arg1 || arg1[0] == '\0' )
   {
      send_to_char( "Usage: cedit save cmdtable\r\n", ch );
      if( get_trust( ch ) >= PERM_HEAD )
      {
         send_to_char( "Usage: cedit save cmdtable\r\n", ch );
         send_to_char( "Usage: cedit <command> create [code]\r\n", ch );
         send_to_char( "Usage: cedit <command> [field] [values]\r\n", ch );
         send_to_char( "   Fields: delete show raise lower list perm position log code flags\r\n", ch );
      }
      return;
   }

   if( get_trust( ch ) >= PERM_HEAD && !str_cmp( arg1, "save" ) && !str_cmp( arg2, "cmdtable" ) )
   {
      save_commands( );
      send_to_char( "Saved.\r\n", ch );
      return;
   }

   command = find_command( arg1 );
   if( get_trust( ch ) >= PERM_HEAD && !str_cmp( arg2, "create" ) )
   {
      /* Reget the command but this time check for the exact command */
      if( ( command = find_exact_command( arg1 ) ) )
      {
         send_to_char( "That command already exists!\r\n", ch );
         return;
      }
      CREATE( command, CMDTYPE, 1 );
      command->lag_count = 0; /* FB */
      command->name = STRALLOC( arg1 );
      command->perm = get_trust( ch );
      if( *argument )
         one_argument( argument, arg2 );
      else
         snprintf( arg2, sizeof( arg2 ), "do_%s", arg1 );
      command->do_fun = skill_function( arg2 );
      command->fun_name = STRALLOC( arg2 );
      add_command( command );
      send_to_char( "Command added.\r\n", ch );
      if( command->do_fun == skill_notfound )
         ch_printf( ch, "Code %s not found.  Set to no code.\r\n", arg2 );
      return;
   }

   if( !command )
   {
      send_to_char( "Command not found.\r\n", ch );
      return;
   }
   else if( command->perm > get_trust( ch ) )
   {
      send_to_char( "You can't touch this command.\r\n", ch );
      return;
   }

   if( !arg2 || arg2[0] == '\0' || !str_cmp( arg2, "show" ) )
   {
      ch_printf( ch, "Command:  %s\r\n", command->name );
      ch_printf( ch, "Perm:     %s\r\n", perms_flag[command->perm] );
      ch_printf( ch, "Position: %s\r\n", pos_names[command->position] );
      ch_printf( ch, "Log:      %d\r\n", command->log );
      ch_printf( ch, "Code:     %s\r\n", command->fun_name );
      ch_printf( ch, "Flags:    %s\r\n", ext_flag_string( &command->flags, cmd_flags ) );
      if( command->userec.num_uses )
         send_timer( &command->userec, ch );
      return;
   }

   if( get_trust( ch ) < PERM_HEAD )
   {
      do_cedit( ch, "" );
      return;
   }

   if( !str_cmp( arg2, "raise" ) )
   {
      CMDTYPE *tmp, *tmp_next;
      int hash = command->name[0] % 126;

      if( ( tmp = command_hash[hash] ) == command )
      {
         send_to_char( "That command is already at the top.\r\n", ch );
         return;
      }
      if( tmp->next == command )
      {
         command_hash[hash] = command;
         tmp_next = tmp->next;
         tmp->next = command->next;
         command->next = tmp;
         ch_printf( ch, "Moved %s above %s.\r\n", command->name, command->next->name );
         return;
      }
      for( ; tmp; tmp = tmp->next )
      {
         tmp_next = tmp->next;
         if( tmp_next->next == command )
         {
            tmp->next = command;
            tmp_next->next = command->next;
            command->next = tmp_next;
            ch_printf( ch, "Moved %s above %s.\r\n", command->name, command->next->name );
            return;
         }
      }
      send_to_char( "ERROR -- Not Found!\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "lower" ) )
   {
      CMDTYPE *tmp, *tmp_next;
      int hash = command->name[0] % 126;

      if( !command->next )
      {
         send_to_char( "That command is already at the bottom.\r\n", ch );
         return;
      }
      tmp = command_hash[hash];
      if( tmp == command )
      {
         tmp_next = tmp->next;
         command_hash[hash] = command->next;
         command->next = tmp_next->next;
         tmp_next->next = command;

         ch_printf( ch, "Moved %s below %s.\r\n", command->name, tmp_next->name );
         return;
      }
      for( ; tmp; tmp = tmp->next )
      {
         if( tmp->next == command )
         {
            tmp_next = command->next;
            tmp->next = tmp_next;
            command->next = tmp_next->next;
            tmp_next->next = command;

            ch_printf( ch, "Moved %s below %s.\r\n", command->name, tmp_next->name );
            return;
         }
      }
      send_to_char( "ERROR -- Not Found!\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "list" ) )
   {
      CMDTYPE *tmp;
      int hash = command->name[0] % 126;
      int col = 0;

      pager_printf( ch, "Priority placement for [%s]:\r\n", command->name );
      for( tmp = command_hash[hash]; tmp; tmp = tmp->next )
      {
         pager_printf( ch, "  %s%-12s", tmp == command ? "&[green]" : "&[plain]", tmp->name );
         if( ++col == 6 )
         {
            send_to_pager( "\r\n", ch );
            col = 0;
         }
      }
      if( col != 0 )
         send_to_pager( "\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "delete" ) )
   {
      unlink_command( command );
      free_command( command );
      send_to_char( "Deleted.\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "code" ) )
   {
      DO_FUN *fun = skill_function( argument );

      if( fun == skill_notfound )
      {
         send_to_char( "Code not found.\r\n", ch );
         return;
      }
      command->do_fun = fun;
      STRSET( command->fun_name, argument );
      send_to_char( "Code Set.\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "perm" ) )
   {
      int level;

      if( is_number( argument ) )
         level = atoi( argument );
      else
         level = get_flag( argument, perms_flag, PERM_MAX );
      if( level < 0 || level > get_trust( ch ) || level >= PERM_MAX )
      {
         send_to_char( "Permission out of range.\r\n", ch );
         return;
      }
      command->perm = level;
      send_to_char( "Perm Set.\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "log" ) )
   {
      int clog = atoi( argument );

      if( clog < 0 || clog > LOG_COMM )
      {
         send_to_char( "Log out of range.\r\n", ch );
         return;
      }
      command->log = clog;
      send_to_char( "Log Set.\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "position" ) )
   {
      int position;

      position = get_flag( argument, pos_names, POS_MAX );
      if( position < 0 || position > POS_DRAG )
      {
         send_to_char( "Position out of range.\r\n", ch );
         return;
      }
      command->position = position;
      send_to_char( "Position Set.\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "flags" ) )
   {
      int value;
      char flag[MSL];

      while( argument && argument[0] != '\0' )
      {
         argument = one_argument( argument, flag );
         value = get_flag( flag, cmd_flags, CMD_MAX );
         if( value < 0 || value >= CMD_MAX )
            ch_printf( ch, "Unknown flag %s.\r\n", flag );
         else
            xTOGGLE_BIT( command->flags, value );
      }
      send_to_char( "Flags Set.\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "name" ) )
   {
      bool relocate;
      CMDTYPE *checkcmd;

      one_argument( argument, arg1 );
      if( !arg1 || arg1[0] == '\0' )
      {
         send_to_char( "Can't clear name field!\r\n", ch );
         return;
      }
      if( ( checkcmd = find_exact_command( arg1 ) ) )
      {
         ch_printf( ch, "There is already a command named %s.\r\n", arg1 );
         return;
      }
      if( arg1[0] != command->name[0] )
      {
         unlink_command( command );
         relocate = true;
      }
      else
         relocate = false;
      STRSET( command->name, arg1 );
      if( relocate )
         add_command( command );
      send_to_char( "Name Set.\r\n", ch );
      return;
   }

   do_cedit( ch, "" );
}

CMDF( do_commands )
{
   char arg[MIL], arg2[MIL];
   int col = 0, cnt = 0, hash, level, minlevel, maxlevel;
   bool found, dislevel, changecolor = true, showhidden = false;
   CMDTYPE *command;

   set_pager_color( AT_PLAIN, ch );
   argument = one_argument( argument, arg );
   if( arg && arg[0] != '\0' && !str_cmp( arg, "showhidden" ) && get_trust( ch ) >= PERM_IMM )
   {
      argument = one_argument( argument, arg );
      showhidden = true;
   }
   argument = one_argument( argument, arg2 );
   if( arg && arg[0] != '\0' && is_number( arg ) )
      minlevel = URANGE( 0, atoi( arg ), PERM_MAX );
   else
      minlevel = 0;
   if( arg2 && arg2[0] != '\0' && is_number( arg2 ) )
      maxlevel = URANGE( minlevel, atoi( arg2 ), MAX_LEVEL );
   else if( arg && arg[0] != '\0' && is_number( arg ) )
      maxlevel = minlevel;
   else
      maxlevel = ( PERM_MAX - 1 );

   found = false;
   for( level = minlevel; level <= maxlevel; level++ )
   {
      dislevel = true;
      col = 0;
      for( hash = 0; hash < 126; hash++ )
      {
         for( command = command_hash[hash]; command; command = command->next )
         {
            if( command->perm != level )
               continue;
            if( command->perm > get_trust( ch ) )
               continue;
            if( arg && arg[0] != '\0' && !is_number( arg ) && command->name && command->name[0] != '\0'
            && str_prefix( arg, command->name ) )
               continue;
            if( xIS_SET( command->flags, CMD_FLAG_NOSHOW ) && !showhidden )
               continue;
            if( !is_npc( ch ) && xIS_SET( command->flags, CMD_FLAG_NPC ) && !showhidden )
               continue;
            if( is_npc( ch ) && xIS_SET( command->flags, CMD_FLAG_PC ) && !showhidden )
               continue;
            found = true;
            cnt++;
            if( dislevel )
            {
               dislevel = false;
               pager_printf( ch, "\r\n&W[Permission &C%s&W]\r\n", perms_flag[level] );
               changecolor = true;
            }
            if( xIS_SET( command->flags, CMD_FLAG_NOSHOW )
            || xIS_SET( command->flags, CMD_FLAG_NPC )
            || xIS_SET( command->flags, CMD_FLAG_PC ) )
            {
               send_to_pager( "&R", ch );
               changecolor = true;
            }
            else if( changecolor )
            {
               send_to_pager( "&C", ch );
               changecolor = false;
            }
            pager_printf( ch, "%-16s", command->name );
            if( ++col == 5 )
            {
               send_to_pager( "\r\n", ch );
               col = 0;
            }
         }
      }
      if( col != 0 )
         send_to_pager( "\r\n", ch );
   }
   if( col != 0 )
      send_to_pager( "\r\n", ch );

   if( arg && arg[0] != '\0' )
   {
      if( is_number( arg ) )
      {
         if( !found )
            pager_printf( ch, "&WNo command found from level &C%d &Wto &C%d&W.&D\r\n", minlevel, maxlevel );
         else
            pager_printf( ch, "&C%d &Wcommands found from level &C%d &Wto &C%d&W.&D\r\n", cnt, minlevel, maxlevel );
      }
      else
      {
         if( !found )
            pager_printf( ch, "&WNo command found under &C%s&W.&D\r\n", arg );
         else
            pager_printf( ch, "&C%d &Wcommands found under &C%s&W.&D\r\n", cnt, arg );
      }
   }
   else
   {
      if( !found )
         send_to_pager( "&WNo commands found at all.&D\r\n", ch );
      else
         pager_printf( ch, "&C%d &Wcommands found in all.&D\r\n", cnt );
   }
}

CMDF( do_restrict )
{
   char arg[MIL], arg2[MIL], buf[MSL];
   short level, hash;
   CMDTYPE *cmd;
   bool found = false, canrestrict = false;

   if( !ch )
      return;

   set_char_color( AT_IMMORT, ch );

   argument = one_argument( argument, arg );
   if( !arg || arg[0] == '\0' )
   {
      send_to_char( "Restrict which command?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg2 );
   if( !arg || arg2[0] == '\0' )
      level = get_trust( ch );
   else
      level = get_flag( arg2, perms_flag, PERM_MAX );

   if( level < 0 || level > get_trust( ch ) )
   {
      send_to_char( "You can't restrict it to that permission level.\r\n", ch );
      return;
   }
   level = URANGE( 0, level, get_trust( ch ) );

   hash = arg[0] % 126;
   for( cmd = command_hash[hash]; cmd; cmd = cmd->next )
   {
      if( !str_prefix( arg, cmd->name ) )
      {
         found = true;
         if( cmd->perm <= get_trust( ch ) )
         {
            canrestrict = true;
            break;
         }
      }
   }

   if( !found )
      ch_printf( ch, "No command found for (%s).\r\n", arg );
   else if( !canrestrict )
      send_to_char( "You may not restrict that command.\r\n", ch );
   else
   {
      if( !str_prefix( arg2, "show" ) )
      {
         snprintf( buf, sizeof( buf ), "%s show", cmd->name );
         do_cedit( ch, buf );
         return;
      }
      cmd->perm = level;
      ch_printf( ch, "You restrict %s to %s\r\n", cmd->name, perms_flag[cmd->perm] );
      log_printf( "%s restricting %s to %s", ch->name, cmd->name, perms_flag[cmd->perm] );
   }
}