cm3/
cm3/clans/
cm3/mudprogs/
cm3/player/a/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			 Command interpretation module			    *
 ****************************************************************************/

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


/*
 * Externals
 */
void refresh_page( CHAR_DATA *ch );
void subtract_times( struct timeval *etime, struct timeval *stime );

bool	check_channel	args( ( CHAR_DATA *ch, char *command, char *argument ) );

bool	check_social	args( ( CHAR_DATA *ch, char *command,
			    char *argument ) );
bool    check_alias     args( ( CHAR_DATA *ch, char *command, char *argument ) );



/*
 * Log-all switch.
 */
bool				fLogAll		= FALSE;


CMDTYPE	   *command_hash[126];	/* hash table for cmd_table */
SOCIALTYPE *social_index[27];   /* hash table for socials   */

/*
 * Character not in position for command?
 */
bool check_pos( CHAR_DATA *ch, sh_int position )
{
    if (!IS_NPC(ch) && IS_SET(ch->pcdata->permissions, PERMIT_ADMIN))
	return TRUE;

    if ( IS_NPC( ch ) && ch->position > 3 ) /*Band-aid alert?  -- Blod*/
      return TRUE;

	if (ch->wait && position > POS_CHANNEL) {
		ch_printf(ch, "You are still %s.\n\r", ch->last_taken);
		return FALSE;
	}

    if ( ch->position < position )
    {
	switch( ch->position )
	{
	case POS_DEAD:
	    send_to_char( "A little difficult to do when you are DEAD...\n\r", ch );
	    break;

	case POS_CHANNEL:
	    send_to_char( "You are busy channeling.\n\r", ch );
	    break;

	case POS_INCAP:
	    send_to_char( "You are hurt far too bad for that.\n\r", ch );
	    break;

	case POS_STUNNED:
	    send_to_char( "You are unconscious.\n\r", ch );
	    break;

	case POS_SLEEPING:
	    send_to_char( "In your dreams, or what?\n\r", ch );
	    break;

	case POS_RESTING:
	    send_to_char( "Nah... You feel too relaxed...\n\r", ch);
	    break;

	case POS_SITTING:
	    send_to_char( "You can't do that sitting down.\n\r", ch);
	    break;

    	case POS_KNEELING:
	    send_to_char( "You should get up off your knees first.\n\r", ch);
	    break;

	case POS_SQUATTING:
	    send_to_char( "Maybe you should stand up first?\n\r", ch);
	    break;

	case POS_SWIMMING:
	    send_to_char( "You're too busy concentrating on swimming.\n\r", ch);
	    break;

	case POS_FLYING:
	    send_to_char("You're too busy concentrating on flying.\n\r", ch);
	    break;

	}
	return FALSE;
    }
    return TRUE;
}

extern char lastplayercmd[MAX_INPUT_LENGTH*2];

/*
 * The main entry point for executing commands.
 * Can be recursively called from 'at', 'order', 'force'.
 */
void interpret( CHAR_DATA *ch, char *argument, bool forced )
{
    char command[MAX_INPUT_LENGTH];
    char logline[MAX_INPUT_LENGTH];
    char logname[MAX_INPUT_LENGTH];
    TIMER *timer = NULL;
    CMDTYPE *cmd = NULL;
    int loglvl;
    bool found;
    struct timeval time_used;
    long tmptime;


    if ( !ch )
    {
	bug( "interpret: null ch!", 0 );
	return;
    }

    if ( !ch->in_room )
    {
    	bug( "interpret: null in_room!", 0 );
	if (!IS_NPC(ch)) char_to_room(ch, 
get_room_index(ROOM_VNUM_LIMBO));
	send_to_char("You are in the middle of nowhere.\n\r", ch);
	return;
    }
	
	found = FALSE;
    if ( ch->substate == SUB_REPEATCMD )
    {
	DO_FUN *fun;

	if ( (fun=ch->last_cmd) == NULL )
	{
	    ch->substate = SUB_NONE;
	    bug( "interpret: SUB_REPEATCMD with NULL last_cmd", 0 );
	    return;
	}
	else
	{
	    int x;

	    /*
	     * yes... we lose out on the hashing speediness here...
	     * but the only REPEATCMDS are wizcommands (currently)
	     */
	    for ( x = 0; x < 126; x++ )
	    {
		for ( cmd = command_hash[x]; cmd; cmd = cmd->next )
		   if ( cmd->do_fun == fun )
		   {
			found = TRUE;
			break;
		   }
		if ( found )
		   break;
	    }
	    if ( !found )
	    {
		cmd = NULL;
		bug( "interpret: SUB_REPEATCMD: last_cmd invalid", 0 );
		return;
	    }
	    sprintf( logline, "(%s) %s", cmd->name, argument );
	}
    }

    if ( !cmd )
    {
	/* Changed the order of these ifchecks to prevent crashing. */
	if ( !argument || !strcmp(argument,"") ) 
	{
	    bug( "interpret: null argument!", 0 );
	    return;
	}

	/*
	 * Strip leading spaces.
	 */
	while ( isspace(*argument) )
	    argument++;
	if ( argument[0] == '\0' )
	    return;

	/*
	 * Implement freeze command.
	 */
	if ( !IS_NPC(ch) && xIS_SET(ch->act, PLR_FREEZE) )
	{
	    send_to_char( "You're totally frozen!\n\r", ch );
	    return;
	}

	/*
	 * Grab the command word.
	 * Special parsing so ' can be a command,
	 *   also no spaces needed after punctuation.
	 */
	strcpy( logline, argument );
	if ( !isalpha(argument[0]) && !isdigit(argument[0]) )
	{
	    command[0] = argument[0];
	    command[1] = '\0';
	    argument++;
	    while ( isspace(*argument) )
		argument++;
	}
	else
	    argument = one_argument( argument, command );

	for ( cmd = command_hash[LOWER(command[0])%126]; cmd; cmd = cmd->next ) {
	    if ( !str_prefix( command, cmd->name )) {
	    	if (!cmd->permit) {
			found = TRUE; break;
	    	} if (IS_NPC(ch) && cmd->permit == PERMIT_MOB) {
			found = TRUE; break;
		} if (!IS_NPC(ch) && IS_SET(ch->pcdata->permissions, cmd->permit)) {
			found = TRUE; break;
		}
	    }
	}

	/* Catch being forced to change password etc */
	if (forced && found && cmd->log == LOG_NEVER)
		return;

	if (forced && found && cmd->permit)
		return;

	/*
	 * Turn off afk bit when any command performed.
	 * don't turn it off if they were forced -keo
	 */
	if ( xIS_SET ( ch->act, PLR_AFK)  && (str_cmp(command, "AFK")) && !forced )
	{
	    xREMOVE_BIT( ch->act, PLR_AFK );
     	    act( AT_GREY, "$n is no longer idle.", ch, NULL, NULL, TO_CANSEE );
            send_to_char("You are no longer idle.\r\n", ch);
	}
    }

    /*
     * Log and snoop.
     */
    sprintf( lastplayercmd, "%s used %s", ch->name, logline );

    if ( found && cmd->log == LOG_NEVER )
	strcpy( logline, "XXXXXXXX XXXXXXXX XXXXXXXX" );

    loglvl = found ? cmd->log : LOG_NORMAL;

    if ( ( !IS_NPC(ch) && xIS_SET(ch->act, PLR_LOG) )
    ||   fLogAll
    ||	 loglvl == LOG_BUILD
    ||   loglvl == LOG_HIGH
    ||   loglvl == LOG_ALWAYS )
    {
        /* Added by Narn to show who is switched into a mob that executes
           a logged command.  Check for descriptor in case force is used. */
        if ( ch->desc && ch->desc->original ) 
          sprintf( log_buf, "Log %s (%s): %s", ch->name,
                   ch->desc->original->name, logline );
        else
          sprintf( log_buf, "Log %s: %s", ch->name, logline );

	/*
	 * Make it so a 'log all' will send most output to the log
	 * file only, and not spam the log channel to death	-Thoric
	 */
	if ( fLogAll && loglvl == LOG_NORMAL
	&&  (IS_NPC(ch) || !xIS_SET(ch->act, PLR_LOG)) )
	  loglvl = LOG_ALL;

	  log_string_plus( log_buf, loglvl, 0 );
    }

    if ( ch->desc && ch->desc->snoop_by )
    {
  	sprintf( logname, "%s", ch->name);
	write_to_buffer( ch->desc->snoop_by, logname, 0 );
	write_to_buffer( ch->desc->snoop_by, "% ",    2 );
	write_to_buffer( ch->desc->snoop_by, logline, 0 );
	write_to_buffer( ch->desc->snoop_by, "\n\r",  2 );
    }

    /* check for a timer delayed command (search, dig, detrap, etc) */
    if ( (timer=get_timerptr(ch, TIMER_DO_FUN)) != NULL )
    {
	int tempsub;

	tempsub = ch->substate;
	ch->substate = SUB_TIMER_DO_ABORT;
	(timer->do_fun)(ch,"");
	if ( char_died(ch) )
	    return;
	if ( ch->substate != SUB_TIMER_CANT_ABORT )
	{
	    ch->substate = tempsub;
	    extract_timer( ch, timer );
	}
	else
	{
	    ch->substate = tempsub;
	    return;
	}
    }


    /*
     * Look for command in skill and socials table.
     */
    if ( !found )
    {
	if ( !check_alias  ( ch, command, argument )
	&&   !check_skill  ( ch, command, argument )
	&&   !check_social ( ch, command, argument )
	&&   !check_channel( ch, command, argument )
#ifdef I3
	&&   !I3_command_hook( ch, command, argument )
#endif
#ifdef IMC
	&&   !imc_command_hook( ch, command, argument )
#endif
		 )
	{
	    EXIT_DATA *pexit;

	    /* check for an auto-matic exit command */
	    if ( (pexit = find_door( ch, command, TRUE )) != NULL
	    &&   IS_SET( pexit->exit_info, EX_xAUTO ))
	    {
		if ( IS_SET(pexit->exit_info, EX_CLOSED)
		&& (!IS_AFFECTED(ch, AFF_PASS_DOOR)
		||   IS_SET(pexit->exit_info, EX_NOPASSDOOR)) )
		{
			/* If they've set outputprefix, spam it here! -- Scion */

			if (!IS_NPC(ch) && ch->pcdata->outputprefix) {
				send_to_char(ch->pcdata->outputprefix, ch);
				send_to_char("\r\n",ch);
			}

			if ( !IS_SET( pexit->exit_info, EX_SECRET ) )
		    act( AT_PLAIN, "The $d is closed.", ch, NULL, pexit->keyword, TO_CHAR );
		  else
		    send_to_char( "You cannot do that here.\n\r", ch );
		  
		    /* If they've set outputsuffix, spam it here! -- Scion */
			if (!IS_NPC(ch) && ch->pcdata->outputsuffix) {
				send_to_char(ch->pcdata->outputsuffix, ch);
				send_to_char("\r\n", ch);
			}
		  
		  return;
		}
		move_char( ch, pexit, 0 );
	    /* If they've set outputsuffix, spam it here! -- Scion */
		if (!IS_NPC(ch) && ch->pcdata->outputsuffix) {
			send_to_char(ch->pcdata->outputsuffix, ch);
			send_to_char("\r\n", ch);
		}

		return;
	    }
	    huh(ch);
	    /* If they've set outputsuffix, spam it here! -- Scion */
		if (!IS_NPC(ch) && ch->pcdata->outputsuffix) {
			send_to_char(ch->pcdata->outputsuffix, ch);
			send_to_char("\r\n", ch);
		}
	}
	return;
    }

    /*
     * Character not in position for command?
     */
    if ( !check_pos( ch, cmd->position ) )
	return;

    /* If they've set outputprefix, spam it here! -- Scion */

	if (!IS_NPC(ch) && ch->pcdata->outputprefix) {
		send_to_char(ch->pcdata->outputprefix, ch);
		send_to_char("\r\n",ch);
	}
	
    /*
     * Dispatch the command.
     */

    ch->prev_cmd = ch->last_cmd;    /* haus, for automapping */
    ch->last_cmd = cmd->do_fun;
    start_timer(&time_used);
    (*cmd->do_fun) ( ch, argument );
    end_timer(&time_used);

	/* Command's done, send the outputsuffix if they want it -- Scion */

	if (!IS_NPC(ch) && ch->pcdata->outputsuffix) {
		send_to_char(ch->pcdata->outputsuffix, ch);
		send_to_char("\r\n", ch);
	}

    /*
     * Update the record of how many times this command has been used (haus)
     */
    update_userec(&time_used, &cmd->userec);
    tmptime = UMIN(time_used.tv_sec,19) * 1000000 + time_used.tv_usec;

    /* laggy command notice: command took longer than 1.5 seconds */
    if ( tmptime > 1500000 )
    {
#ifdef sun
        sprintf(log_buf, "[*****] LAG: %s: %s %s (R:%d S:%d.%06d)", ch->name,
                cmd->name, (cmd->log == LOG_NEVER ? "XXX" : argument),
		ch->in_room ? ch->in_room->vnum : 0,
		time_used.tv_sec, time_used.tv_usec );
#else
        sprintf(log_buf, "[*****] LAG: %s: %s %s (R:%d S:%ld.%06ld)", ch->name,
                cmd->name, (cmd->log == LOG_NEVER ? "XXX" : argument),
		ch->in_room ? ch->in_room->vnum : 0,
		time_used.tv_sec, time_used.tv_usec );
#endif
	log_string_plus(log_buf, LOG_NORMAL, 0);
	cmd->lag_count++;	/* count the lag flags */
    }

	tail_chain( );
}

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

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

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

    return NULL;
}

SOCIALTYPE *find_social( char *command )
{
    SOCIALTYPE *social;
    int hash;

    if ( command[0] < 'a' || command[0] > 'z' )
	hash = 0;
    else
	hash = (command[0] - 'a') + 1;

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

    return NULL;
}

bool check_social( CHAR_DATA *ch, char *command, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;
    SOCIALTYPE *social;
    char adj[MAX_STRING_LENGTH];

    if ( (social=find_social(command)) == NULL )
	return FALSE;

	/* If they've set outputprefix, spam it here! -- Scion */

	if (!IS_NPC(ch) && ch->pcdata->outputprefix) {
		send_to_char(ch->pcdata->outputprefix, ch);
		send_to_char("\r\n",ch);
	}
    
    if ( !IS_NPC(ch) && xIS_SET(ch->act, PLR_NO_EMOTE) )
    {
	send_to_char( "You are anti-social!\n\r", ch );
	return TRUE;
    }
   
    if ( IS_AFFECTED(ch, AFF_SILENT) && IS_SET(social->flags, SOC_AUDITORY))
    {
	send_to_char( "You make no sound.\n\r", ch );
	return TRUE;
    }

    switch ( ch->position ) {
    case POS_STUNNED:
	send_to_char( "You are too stunned to do that.\n\r", ch );
	return TRUE;

    case POS_SLEEPING:
	if (IS_SET(social->flags, SOC_SLEEP))
	    break;
	send_to_char( "In your dreams, or what?\n\r", ch );
	return TRUE;

    }

    argument = one_argument( argument, arg );
    argument = one_argument( argument, adj );
    victim = NULL;
	
    if ( arg[0] == '\0' ) {
	act( AT_SOCIAL, social->others_no_arg, 
		ch, social->adj, victim, TO_ROOM);
	act( AT_SOCIAL, social->char_no_arg,   
		ch, social->adj, victim, TO_CHAR );
    } else if ( ( victim = get_char_room( ch, arg ) ) == NULL ) {
    	if(!victim) {
        act( AT_SOCIAL, social->others_no_arg, ch, arg, victim, TO_ROOM);
        act( AT_SOCIAL, social->char_no_arg,   ch, arg, victim, TO_CHAR);
	}
    }
    else if ( victim == ch ) {
	if (adj && adj[0] != '\0') {
		if (IS_SET(social->flags, SOC_VISUAL))
			act( AT_SOCIAL, social->others_auto,   
				ch, adj, victim, TO_CANSEE);
		else
			act( AT_SOCIAL, social->others_auto,   
				ch, adj, victim, TO_ROOM);
		act( AT_SOCIAL, social->char_auto,     
			ch, adj, victim, TO_CHAR);
	} else {
		if (IS_SET(social->flags, SOC_VISUAL))
                act( AT_SOCIAL, social->others_auto,
                        ch, social->adj, victim, TO_CANSEE);
		else
                act( AT_SOCIAL, social->others_auto,
                        ch, social->adj, victim, TO_ROOM);
                act( AT_SOCIAL, social->char_auto,
                        ch, social->adj, victim, TO_CHAR);
	}
    } else {
	if (adj && adj[0] != '\0') {
		act( AT_SOCIAL, social->others_found,  
			ch, adj, victim, TO_NOTVICT);
		act( AT_SOCIAL, social->char_found,    
			ch, adj, victim, TO_CHAR);
		act( AT_SOCIAL, social->vict_found,    
			ch, adj, victim, TO_VICT);
	} else {
                act( AT_SOCIAL, social->others_found,
                        ch, social->adj, victim, TO_NOTVICT);
                act( AT_SOCIAL, social->char_found,
                        ch, social->adj, victim, TO_CHAR);
                act( AT_SOCIAL, social->vict_found,
                        ch, social->adj, victim, TO_VICT);
	}
	/* Make mobs react accordingly... 
	 * Don't respond to other mobs, could get into infinte loop.
	 */
	if (IS_NPC(victim)
	&& !IS_NPC(ch)
	&&  IS_SET(victim->xflags, PART_HANDS)
	&&  can_see(victim, ch)) {
		if (IS_SET(social->flags, SOC_POLITE)) {
			switch(number_range(1,3)) {
			case 1: check_social(victim, "smile", ch->name); break;
			case 2: check_social(victim, "bow", ch->name); break;
			case 3: check_social(victim, "pat", ch->name); break;
			}
		} else if (IS_SET(social->flags, SOC_SEXUAL)) {
			if (victim->sex == ch->sex)
			switch(number_range(1,6)) {
			case 1: check_social(victim, "slap", ch->name); break;
			case 2: check_social(victim, "gasp", ch->name); break;
			case 3: check_social(victim, "puke", ch->name); break;
			case 4: check_social(victim, "edge", ch->name); break;
			case 5: check_social(victim, "smack", ch->name); break;
			case 6: check_social(victim, "thwap", ch->name); break;
			}
			else
			switch(number_range(1,5)) {
			case 1: check_social(victim, "kiss", ch->name); break;
			case 2: check_social(victim, "hug", ch->name); break;
			case 3: check_social(victim, "smile", ch->name); break;
			case 4: check_social(victim, "cuddle", ch->name); break;
			case 5: check_social(victim, "snuggle", ch->name); break;
			}
		} else if (IS_SET(social->flags, SOC_RUDE)) {
			switch(number_range(1,3)) {
			case 1: check_social(victim, "slap", ch->name); break;
			case 2: check_social(victim, "smack", ch->name); break;
			case 3: check_social(victim, "growl", ch->name); break;
			}
		} else if (IS_SET(social->flags, SOC_ANNOYING)) {
			switch(number_range(1,3)) {
			case 1: check_social(victim, "eyebrow", ch->name); break;
			case 2: check_social(victim, "poke", ch->name); break;
			case 3: check_social(victim, "smirk", ch->name); break;
			}
		}
	}
    }

    /* If they've set outputsuffix, spam it here! -- Scion */

	if (!IS_NPC(ch) && ch->pcdata->outputsuffix) {
		send_to_char(ch->pcdata->outputsuffix, ch);
		send_to_char("\r\n", ch);
	}

	return TRUE;
}

/*
 * Alias Command Parser
 * June 24, 1999 - Fixed rather annoying wait state bug in alias parser - Justice@AaernMUD
 */
 bool check_alias( CHAR_DATA *ch, char *command, char *argument ) {
	 ALIAS_DATA *alias;
	 ALIAS_QUEUE *queue;
	 ALIAS_QUEUE *new_queue;
	 ALIAS_QUEUE *first;
	 char buf[MAX_STRING_LENGTH];
	 int str=0;
	 int i=0;
	 int aliaslimit=0; /* To prevent recursive alias stacking -- Scion */

	 if (IS_NPC(ch))
		 return FALSE;

	 for (alias=ch->pcdata->first_alias; alias; alias=alias->next) {
		 if (!str_cmp(alias->name, command))
			 break;
	 }

	 if (!alias)
		 return FALSE;

	 queue=ch->pcdata->alias_queue;
	 first=ch->pcdata->alias_queue;

	 while (queue && queue->next)
		 queue=queue->next;

	 buf[0]='\0';

	 for ( str = 0; alias->alias[str] != '\0'; str++ ) {
		 aliaslimit++;
		 if (aliaslimit>=1000) {
			 sprintf(buf, "Alias limit reached on character %s!", ch->name);
			 ch->pcdata->alias_queue=NULL;
			 return TRUE;
		 }

		 if (alias->alias[str]==';' && strlen(buf) > 0) {

			 if ( sysdata.alias_wait != -1 )
                  WAIT_STATE( ch, sysdata.alias_wait + ch->wait );
              else
                  WAIT_STATE( ch, 4 + ch->wait );

			  /* Add command to the alias queue -- Scion */
			  CREATE( new_queue, ALIAS_QUEUE, 1 );
			  new_queue->next=NULL;
			  new_queue->cmd=str_dup(buf);
			  i=0;

			  if (queue) {
				  queue->next=new_queue;
				  queue=queue->next;
			  } else {
				  queue = new_queue;
				  first = queue;
			  }
		 }
	/*	 else if (alias->alias[str]=='%') {} */
		 else {
			 buf[i]=alias->alias[str];
			 buf[i+1]='\0';
			 i++;
		 }
	 }
	 /* Add the last command to the alias queue -- Scion */
	 CREATE( new_queue, ALIAS_QUEUE, 1 );
	 new_queue->next=NULL;
	 new_queue->cmd=str_dup(buf);
	 i=0;

	 if (queue) {
		queue->next=new_queue;
		queue=queue->next;
	 } else {
		queue = new_queue;
		first = queue;
	 }
	 ch->pcdata->alias_queue=first;
	 return TRUE;
 }

/*
 * Return true if an argument is completely numeric.
 */
bool is_number( char *arg )
{
    if ( *arg == '\0' )
	return FALSE;

    for ( ; *arg != '\0'; arg++ )
    {
	if ( !isdigit(*arg) )
	    return FALSE;
    }

    return TRUE;
}



/*
 * Given a string like 14.foo, return 14 and 'foo'
 */
int number_argument( char *argument, char *arg )
{
    char *pdot;
    int number;

    for ( pdot = argument; *pdot != '\0'; pdot++ )
    {
	if ( *pdot == '.' )
	{
	    *pdot = '\0';
	    number = atoi( argument );
	    *pdot = '.';
	    strcpy( arg, pdot+1 );
	    return number;
	}
    }

    strcpy( arg, argument );
    return 1;
}

/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes.
 */
char *one_argument( char *argument, char *arg_first )
{
    char cEnd;
    sh_int count;

    count = 0;

    while ( isspace(*argument) )
	argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"' )
	cEnd = *argument++;

    while ( *argument != '\0' || ++count >= 255 )
    {
	if ( *argument == cEnd )
	{
	    argument++;
	    break;
	}
	*arg_first = LOWER(*argument);
	arg_first++;
	argument++;
    }
    *arg_first = '\0';

    while ( isspace(*argument) )
	argument++;

    return argument;
}

/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes.  Delimiters = { ' ', '-' }
 */
char *one_argument2( char *argument, char *arg_first )
{
    char cEnd;
    sh_int count;

    count = 0;

    while ( isspace(*argument) )
	argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"' )
	cEnd = *argument++;

    while ( *argument != '\0' || ++count >= 255 )
    {
	if ( *argument == cEnd || *argument == '-' )
	{
	    argument++;
	    break;
	}
	*arg_first = LOWER(*argument);
	arg_first++;
	argument++;
    }
    *arg_first = '\0';

    while ( isspace(*argument) )
	argument++;

    return argument;
}

/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes. Retains case.
 */
char *one_argument_retain_case( char *argument, char *arg_first )
{
    char cEnd;
    sh_int count;

    count = 0;

    while ( isspace(*argument) )
	argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"' )
	cEnd = *argument++;

    while ( *argument != '\0' || ++count >= 255 )
    {
	if ( *argument == cEnd )
	{
	    argument++;
	    break;
	}
	*arg_first = *argument;
	arg_first++;
	argument++;
    }
    *arg_first = '\0';

    while ( isspace(*argument) )
	argument++;

    return argument;
}


void do_timecmd( CHAR_DATA *ch, char *argument )
{
  struct timeval stime;
  struct timeval etime;
  static bool timing;
  extern CHAR_DATA *timechar;
  char arg[MAX_INPUT_LENGTH];
  
  send_to_char("Timing\n\r",ch);
  if ( timing )
    return;
  one_argument(argument, arg);
  if ( !*arg )
  {
    send_to_char( "No command to time.\n\r", ch );
    return;
  }
  if ( !str_cmp(arg, "update") )
  {
    if ( timechar )
      send_to_char( "Another person is already timing updates.\n\r", ch );
    else
    {
      timechar = ch;
      send_to_char( "Setting up to record next update loop.\n\r", ch );
    }
    return;
  }
  set_char_color(AT_PLAIN, ch);
  send_to_char( "Starting timer.\n\r", ch );
  timing = TRUE;
  gettimeofday(&stime, NULL);
  interpret(ch, argument, FALSE);
  gettimeofday(&etime, NULL);
  timing = FALSE;
  set_char_color(AT_PLAIN, ch);
  send_to_char( "Timing complete.\n\r", ch );
  subtract_times(&etime, &stime);
  ch_printf( ch, "Timing took %d.%06d seconds.\n\r",
      etime.tv_sec, etime.tv_usec );
  return;
}

void start_timer(struct timeval *stime)
{
  if ( !stime )
  {
    bug( "Start_timer: NULL stime.", 0 );
    return;
  }
  gettimeofday(stime, NULL);
  return;
}

time_t end_timer(struct timeval *stime)
{
  struct timeval etime;
  
  /* Mark etime before checking stime, so that we get a better reading.. */
  gettimeofday(&etime, NULL);
  if ( !stime || (!stime->tv_sec && !stime->tv_usec) )
  {
    bug( "End_timer: bad stime.", 0 );
    return 0;
  }
  subtract_times(&etime, stime);
  /* stime becomes time used */
  *stime = etime;
  return (etime.tv_sec*1000000)+etime.tv_usec;
}

void send_timer(struct timerset *vtime, CHAR_DATA *ch)
{
  struct timeval ntime;
  int carry;
  
  if ( vtime->num_uses == 0 )
    return;
  ntime.tv_sec  = vtime->total_time.tv_sec / vtime->num_uses;
  carry = (vtime->total_time.tv_sec % vtime->num_uses) * 1000000;
  ntime.tv_usec = (vtime->total_time.tv_usec + carry) / vtime->num_uses;
  ch_printf(ch, "Has been used %d times this boot.\n\r", vtime->num_uses);
  ch_printf(ch, "Time (in secs): min %d.%0.6d; avg: %d.%0.6d; max %d.%0.6d"
      "\n\r", vtime->min_time.tv_sec, vtime->min_time.tv_usec, ntime.tv_sec,
      ntime.tv_usec, vtime->max_time.tv_sec, vtime->max_time.tv_usec);
  return;
}

void update_userec(struct timeval *time_used, struct timerset *userec)
{
  userec->num_uses++;
  if ( !timerisset(&userec->min_time)
  ||    timercmp(time_used, &userec->min_time, <) )
  {
    userec->min_time.tv_sec  = time_used->tv_sec;
    userec->min_time.tv_usec = time_used->tv_usec;
  }
  if ( !timerisset(&userec->max_time)
  ||    timercmp(time_used, &userec->max_time, >) )
  {
    userec->max_time.tv_sec  = time_used->tv_sec;
    userec->max_time.tv_usec = time_used->tv_usec;
  }
  userec->total_time.tv_sec  += time_used->tv_sec;
  userec->total_time.tv_usec += time_used->tv_usec;
  while ( userec->total_time.tv_usec >= 1000000 )
  {
    userec->total_time.tv_sec++;
    userec->total_time.tv_usec -= 1000000;
  }
  return;
}

/*
 *  This function checks the command against the command flags to make
 *  sure they can use the command online.  This allows the commands to be
 *  edited online to allow or disallow certain situations.  May be an idea
 *  to rework this so we can edit the message sent back online, as well as
 *  maybe a crude parsing language so we can add in new checks online without
 *  haveing to hard-code them in.     -- Shaddai   August 25, 1997
 */

void do_alias( CHAR_DATA *ch, char *argument )
{
    ALIAS_DATA *alias = NULL;
    char        arg[MAX_INPUT_LENGTH];

    if ( IS_NPC( ch ) )
    {
        send_to_char( "Mobs can't use aliases.\n\r", ch );
        return;
    }

    if ( !argument || argument[0] == '\0' )
    {
        if ( !ch->pcdata->first_alias )
        {
            send_to_char( "You don't have any aliases.\n\r", ch );
            return;
        }
        else
        {
            int count = 0;

            send_to_char( "You have the following aliases:\n\r", ch );

            for( alias = ch->pcdata->first_alias; alias; alias = alias->next, count++ )
                ch_printf( ch, "%s     %s\n\r", alias->name, alias->alias );

            ch_printf( ch, "You have %d alias%s.\n\r", count, count == 1 ? "" : "es" );
            return;
        }
    }

    argument = one_argument( argument, arg );

    for( alias = ch->pcdata->first_alias; alias; alias = alias->next )
        if ( !str_cmp( arg, alias->name ) ) break;
    if ( !alias )
    {
        if ( argument[0] == '\0' )
        {
            ch_printf( ch, "You don't have any alias called %s.\n\r", arg );
            return;
        }
        CREATE( alias, ALIAS_DATA, 1 );
        alias->name  = strdup( arg );
        alias->alias = strdup( argument );
        LINK( alias, ch->pcdata->first_alias, ch->pcdata->last_alias, next, prev );

        send_to_char( "Alias Added:\n\r", ch );
        ch_printf( ch, "%s     %s\n\r", alias->name, alias->alias );
    return;
    }
    if ( argument[0] == '\0' )
    {
        UNLINK( alias, ch->pcdata->first_alias, ch->pcdata->last_alias, next, prev );
        DISPOSE( alias );
        send_to_char( "Alias deleted.\n\r", ch );
        return;
    }

    send_to_char( "Alias Changed.\n\r", ch );
    alias->alias = strdup(argument);
    ch_printf( ch, "%s     %s\n\r", alias->name, alias->alias );
    return;
}