vote-1.01/
/**************************************************************************
*     Voting system designed and written for use on Legend of the Nobles  *
*         by Valnir (valnir@legendofthenobles.com).                       *
*                                                                         *
*     - http://www.legendofthenobles.com                                  *
*     - telnet://game.legendofthebobles.com:5400                          *
*                                                                         *
*     This code is free for use by any person wishing to use it with the  *
*         restriction that this information is kept intact and unchanged. *
*     Original version created on 05-26-2004                              *
**************************************************************************/

#if defined(macintosh)
#include <types.h>
#include <time.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "merc.h"
#include "olc.h"
#include "vote.h"

int		total_polls	= 0;
POLL_DATA	*poll_first;

/* local functions */
void		poll_vote args( ( CHAR_DATA *ch, POLL_DATA *poll, int choice ) );
void		display_results	args( ( CHAR_DATA *ch, POLL_DATA *poll ) );
bool		can_edit_poll args( ( CHAR_DATA *ch, POLL_DATA *poll ) );
bool		has_voted args( ( CHAR_DATA *ch, POLL_DATA *poll ) );
long		get_raw_time args( ( char *date ) );
int		days_left args( ( POLL_DATA *poll ) );
POLL_DATA *	new_poll args( ( void ) );
POLL_DATA *	get_poll_data args( ( int num ) ) ;
void		poll_compact args( ( POLL_DATA *poll ) );
char	*	format_output args( ( char *string, int spaces ) );

/*
 * poll_vote - actual voting function
 * processes the players vote to see if it is valid
 * and to actually apply the vote
 */
void poll_vote ( CHAR_DATA *ch, POLL_DATA *poll, int choice )
{
    char list[MSL];

    if ( poll != NULL )
    {
	if ( poll->open > current_time || poll->close < current_time )
	{
	    send_to_char( "That poll is not currently open for voting.\n\r", ch );
	    return;
	}

	if ( has_voted( ch, poll ) )
	{
	    send_to_char( "You have already voted on this poll.\n\r", ch );
	    return;
	}

	if ( choice <= 0 || choice > MAX_CHOICE
	|| IS_NULLSTR(poll->choice[choice-1]) )
	{
	    send_to_char( "That is not a valid choice to cast your vote on.\n\r", ch );
	    return;
	}

	poll->votes[choice-1]++; // add vote to poll data record.

	
    // add poll unique id to pfile to ensure the player can't vote again
	sprintf( list, "%s%s%d", ch->pcdata->polls,
	    !IS_NULLSTR(ch->pcdata->polls) ? "|" : "", poll->id );
	free_string( ch->pcdata->polls );
	ch->pcdata->polls = str_dup( list );

	send_to_char( "Thank you for your vote. The current "
	    "results are displayed below.\n\r\n\r", ch );

	display_results( ch, poll );
    }

    return;
}

/*
 * do_poll - main poll function
 * used to pass all poll related requests to sub functions.
 */
void do_poll ( CHAR_DATA *ch, char *argument )
{
    POLL_DATA *poll = NULL;
    char buf[MSL];
    char arg1[MIL];
    char arg2[MIL];
    char arg3[MIL];
    int selected, choice;

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

    // show the list of polls, or how many new polls are available
    if ( argument[0] == '\0' || !str_cmp( argument, "new" ) )
    {
	POLL_DATA *loop;
	char left[MSL];
	int poll_num = 0;
	int new_count = 0;
	bool found = FALSE;

        for ( loop = poll_first; loop != NULL; loop = loop->next )
        {
	    left[0] = '\0';
	    poll_num++;

	    if ( IS_SET( loop->flags, POLL_PUBLISHED )
	    && !IS_SET( loop->flags, POLL_HIDDEN )
	    && !IS_SET( loop->flags, POLL_DELETE ) )
	    {
		if ( !str_prefix( loop->to, "implementors" ) && get_trust(ch) < CREATOR )
		    continue;
		else if ( !str_prefix( loop->to, "immortals" ) && get_trust(ch) < LEVEL_IMMORTAL )
		    continue;

		if ( !str_cmp( argument, "new" ) && has_voted(ch, loop) )
		    continue;
		else if ( !str_cmp( argument, "new" ) )
		{
		    new_count++;
		    found = TRUE;
		    continue;
		}

	        if ( !found )
		    send_to_char( "Available Polls:\n\r", ch );

		if ( days_left(loop) > 0 && !has_voted(ch, loop) )
		    sprintf( left, " (%d days left)", days_left(loop) );

	        sprintf( buf, "%3d%s) %s%s\n\r", poll_num,
		    !has_voted(ch, loop) ? "N" : " ", format_output(loop->topic, 6),
			left[0] != '\0' ? left : "" );
	        send_to_char( buf, ch );
	        found = TRUE;
	    }
        }

	if ( !found )
	    send_to_char("There are no new polls available to vote on.\n\r", ch);
	else if ( new_count > 0 )
	{
	    sprintf( buf, "There %s %d new poll%s to vote on.\n\r",
		new_count > 1 ? "are" : "is", new_count,
		new_count > 1 ? "s" : "" );
	    send_to_char( buf, ch );
	}

	return;
    }

    /*
     * Removing this will void the license agreement.
	 * Be kind and give credit where credit is due.
	 */
    if ( !str_prefix( argument, "credits" ) )
    {
	send_to_char( POLL_CREDITS, ch );
	return;
    }

    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );
    argument = one_argument( argument, arg3 );

    // check for valid syntax
    if ( !is_number(arg1) || ( arg3[0] != '\0' && !is_number(arg3) ) )
    {
	send_to_char( "Syntax: poll [poll #] vote [choice #]\n\r", ch );
	send_to_char( "        poll [poll #]\n\r", ch );
	send_to_char( "        poll [poll #] results\n\r", ch );
	return;
    }

    selected = atoi(arg1);
    choice = atoi(arg3);

    // get the actual poll data
    if ( ( poll = get_poll_data(selected) ) == NULL
    || !IS_SET(poll->flags, POLL_PUBLISHED)
    || IS_SET(poll->flags, POLL_DELETE)
    || IS_SET(poll->flags, POLL_HIDDEN) )
    {
	send_to_char( "That is an invalid poll selection.\n\r", ch );
	return;
    }

    if ( ( !str_prefix( poll->to, "implementors" ) && get_trust(ch) < CREATOR )
    || ( !str_prefix( poll->to, "immortals" ) && get_trust(ch) < LEVEL_IMMORTAL ) )
    {
	send_to_char( "That is an invalid poll selection.\n\r", ch );   
	return;
    }

    if ( arg2[0] != '\0' && !str_prefix( arg2, "results" ) )
	display_results( ch, poll );
    else if ( arg2[0] != '\0' && !str_prefix( arg2, "vote" ) )
	poll_vote( ch, poll, choice );
    else
    {
	sprintf( buf, "[%3d] %s\n\r", selected, format_output(poll->topic, 6) );
	send_to_char( buf, ch );
	send_to_char( "\n\rAvailable voting choices are:\n\r", ch );

	for ( choice = 0; choice < MAX_CHOICE; choice++ )
	{
	    if ( !IS_NULLSTR(poll->choice[choice]) )
	    {
		sprintf( buf, "%2d) %s\n\r",
		    choice + 1, format_output(poll->choice[choice], 4) );
		send_to_char( buf, ch );
	    }
	}

	send_to_char( "\n\rVoting Syntax: poll <poll #> vote <choice #>\n\r", ch );
    }

    return;
}

/*
 * display_results - shows the player the results of the
 * selected poll, including % of votes cast.
 */
void display_results ( CHAR_DATA *ch, POLL_DATA *poll )
{
    char buf[MSL];
    int count, len;
    int votes = 0;

    if ( poll == NULL || poll->topic == NULL )
    {
	send_to_char( "No such poll.\n\r", ch );
	return;
    }

    for ( count = 0; count < MAX_CHOICE; count++ )
	votes += poll->votes[count];

    sprintf( buf, "Results for poll: %s\n\r\n\r", format_output(poll->topic, 18) );
    send_to_char( buf, ch );

    sprintf( buf, "[ Choice ]-----------------------------------------------------[  Votes  ]\n\r" );
    send_to_char( buf, ch );

    for ( count = 0; count < MAX_CHOICE; count++ )
    {
	if ( !IS_NULLSTR(poll->choice[count]) )
	{
	    if ( strlen(poll->choice[count]) > 57 )
		len = 54;
	    else
		len = 57;

	    sprintf( buf, "%2d) %-*.*s%s  %4d (%2d%%)\n\r",
		count + 1, len, len, poll->choice[count], len == 54 ? "..." : "",
		poll->votes[count], votes > 0 ? (poll->votes[count]*100)/votes : 0 );
	    send_to_char( buf, ch );
	}
    }

    sprintf( buf, "\n\rTotal Votes: %d\n\r", votes );
    send_to_char( buf, ch );

    return;
}

/*
 * load_polls - gets the poll data from the 'polls.dat'
 * file in the DATA_DIR.
 */
void load_polls( void )
{
    FILE *fp;
    POLL_DATA *poll = NULL;
    char buf[MSL];
    char *string;
    int choice, vote;
    bool fMatch = FALSE;
    
    sprintf( buf, "%s%s", DATA_DIR, POLL_FILE );
    
    if ( ( fp = fopen( buf, "r" ) ) == NULL )
    {
	sprintf( buf, "Error: %s file not found!", POLL_FILE );
        log_string(buf);
        return;
    }

    poll_first	= NULL;
    top_poll	= 0;
    choice	= 0;
    vote	= 0;

    for (;;)
    {
	string = feof(fp) ? "End" : fread_word(fp);
    
	if ( !str_cmp( string, "End" ) )
            break;
    
	if ( !str_cmp( string, "Topic" ) )
	{
	    POLL_DATA *loop;

	    poll	= new_poll( );
	    choice	= 0;
	    top_poll++;

	    if ( poll_first == NULL )
		poll_first = poll;

	    for ( loop = poll_first; loop != NULL; loop = loop->next )
	    {
		if ( loop == poll )
		   break;

		if ( loop->next == NULL )
		   loop->next = poll;
	    }
	}

	switch ( UPPER(string[0]) )
	{
            case 'T':
		KEY("Topic",	poll->topic,		fread_string(fp) );
		KEY("To",	poll->to,		fread_string(fp) );
		break;
    
            case 'C':
		KEY("Creator",	poll->creator,		fread_string(fp) );
		KEY("Created",	poll->created,		fread_number(fp) );
		KEY("Close",	poll->close,		fread_number(fp) );

		if ( !str_cmp( string, "Ch" ) )
		{
                    if ( choice < MAX_CHOICE )
		    {
                        poll->choice[choice++] = str_dup( fread_string(fp) );
                        fMatch = TRUE;
		    }
		}
		break;

	    case 'I':
		KEY("Id",	poll->id,		fread_number(fp) );
		break;

	    case 'F':
		KEY("Flags",	poll->flags,		fread_flag(fp) );
		break;

	    case 'O':
		KEY("Open",	poll->open,		fread_number(fp) );
		break;

	    case 'V':
		if ( !str_cmp( string, "Votes" ) )
		{
		    for( vote = 0; vote < MAX_CHOICE; vote++ )
			poll->votes[vote]	= fread_number(fp);

		    fMatch = TRUE;
		}
		break;
	}
    }
                    
    if ( !fMatch && poll_first != NULL )
    {
	bug( "load_polls: no match", 0 );
	fread_to_eol(fp);
    }
                 
    fclose(fp);

    return;
}

/*
 * save_polls - used to write the poll data to the 'polls.dat'
 * file in the DATA_DIR. Also calls the compact function to
 * ensure that no 'blank' choices are left in between other
 * choices. Blanks can cause incorrect totals.
 */
void save_polls( CHAR_DATA *ch, char *argument )
{
    FILE *fp;
    POLL_DATA *poll;
    char buf[MIL];
    int i;

    sprintf( buf, "%s%s", DATA_DIR, POLL_FILE );

    if ( !( fp = fopen( buf, "w+" ) ) )
    {
        bug("Open polls: fopen", 0);
        return;
    }

    for ( poll = poll_first; poll != NULL; poll = poll->next )
    {
        if ( poll->topic == NULL || IS_SET( poll->flags, POLL_DELETE ) )
            continue;

	if ( IS_SET( poll->flags, POLL_CHANGED ) )
	    REMOVE_BIT( poll->flags, POLL_CHANGED );
    
	poll_compact(poll);

        fprintf( fp, "\nTopic %s~\n", ( poll->topic == NULL ) ? "" : poll->topic );
	fprintf( fp, "To %s~\n", ( poll->to == NULL ) ? "" : poll->to );
	fprintf( fp, "Creator %s~\n", ( poll->creator == NULL ) ? "" : poll->creator );
	fprintf( fp, "Open %ld\n", poll->open );
	fprintf( fp, "Close %ld\n", poll->close );
	fprintf( fp, "Created %ld\n", poll->created );
	fprintf( fp, "Flags %s\n", print_flags(poll->flags) );
	fprintf( fp, "Id %d\n", poll->id );

        fprintf( fp, "Votes" );
        for ( i = 0; i < MAX_CHOICE; i++ )
            fprintf( fp, " %3d", poll->votes[i] );
        fprintf( fp, "\n" );
        
        i = 0;
        while ( i < MAX_CHOICE )
        {
	    if ( !IS_NULLSTR(poll->choice[i]) )
                fprintf( fp, "Ch %s~\n", poll->choice[i] );

	    i++;
        }
    }
  
    fprintf( fp, "\nEnd\n" );
         
    fclose( fp );
    if ( ch )
        send_to_char( "Polls file written.\n\r", ch );
        
    return;
}

POLL_DATA * poll_free;
      
/*
 * new_poll - create and allocate memory for a new
 * poll in memory. full poll data initialization
 * is done here.
 */
POLL_DATA * new_poll ( void )
{
    POLL_DATA * poll;
    int i;
              
    if ( poll_free )
    {  
        poll		= poll_free;
        poll_free	= poll_free->next;
    }
    else
        poll		= alloc_perm( sizeof( *poll ) );
 
    VALIDATE(poll);
    poll->next		= NULL;
    poll->topic		= str_dup( "" );
    poll->to		= str_dup( "" );
    poll->creator		= str_dup( "" );
    poll->open		= 0;
    poll->close		= 0;
    poll->created		= 0;
    poll->flags		= 0;
    poll->id		= 0;

    for ( i = 0; i < MAX_CHOICE; i++ )
    {
        poll->choice[i]	= str_dup( "" );
        poll->votes[i]	= 0;
    }
        
    return poll;
}

/*
 * do_pedit - OLC style editor for poll data
 * I'm not going to go into each fucntion here,
 * read the PEDIT help in the text file.
 */
void do_pedit(CHAR_DATA *ch, char *argument)
{
    POLL_DATA *poll;
    char arg[MIL];
    int num = 0;

    if ( IS_NPC(ch) )
        return;
    
    if ( argument[0] == '\0' )
    {
        send_to_char( "Syntax : PEdit [poll #]\n\r", ch );
        return;
    }

    if (ch->pcdata->security < 9)
    {
        send_to_char( "PEdit : Insufficient security to edit polls.\n\r", ch );
        return;
    }
    
    argument = one_argument( argument, arg );
    
    if ( !str_cmp( arg, "create" ) )
    {
        if ( ch->pcdata->security < 9 )
        {
          send_to_char( "PEdit : Insuffecient security to create new polls.\n\r",ch);
          return;
        }
    
        if ( argument[0] == '\0' )
        {
          send_to_char( "PEdit: Please specify a topic for the poll.\n\r",ch );
          return;
        }

        pedit_create( ch, argument );
        ch->desc->editor = ED_POLL;
        return;
    }
    else if ( !str_cmp( arg, "list" ) )
    {
	pedit_list( ch, "" );
	return;
    }
    else
    {
        if ( is_number( arg ) )
            num = atoi( arg );
           
        if ( num <= 0 || num > top_poll
	|| ( poll = get_poll_data( num ) ) == NULL )
        {
            send_to_char("PEdit: That poll does not exist.\n\r", ch);
            return;
        }
        
        if ( poll->topic == NULL || poll->topic[0] == '\0' )
        {
            send_to_char( "PEdit: That poll isn't defined yet.\n\r", ch );
            return;
        }
    }     
        
    ch->desc->pEdit = poll;
    ch->desc->editor = ED_POLL;
    return;
}

void pedit( CHAR_DATA *ch, char *argument )
{
    POLL_DATA *poll;
    char arg[MIL];
    char command[MIL];
    int cmd;
            
    strcpy(arg, argument);
    argument = one_argument( argument, command );
     
    EDIT_POLL(ch, poll);
    if (ch->pcdata->security < 9)
    {
        send_to_char("GrEdit: Insufficient security to edit groups.\n\r",ch);
        edit_done(ch);
        return;
    }
    
    if (command[0] == '\0')
    {
        pedit_show(ch, argument);
        return;
    }
    
    if ( !str_cmp(command, "done") )
    {
	if ( IS_SET(poll->flags, POLL_CHANGED) )
	    save_polls( ch, "" );

        edit_done(ch);
        return;
    }

    if ( !str_cmp(command, "help") && argument[0] == '\0' )
    {
	do_help( ch, "pedit" );
	return;
    }

    for (cmd = 0; pedit_table[cmd].name != NULL; cmd++)
    {
        if ( !str_prefix(command, pedit_table[cmd].name) )
        {
	   bool changed;

           changed = (*pedit_table[cmd].olc_fun) (ch, argument);

	   if ( changed )
		SET_BIT(poll->flags, POLL_CHANGED);

           return;
        }
    }
    
    interpret(ch, arg);
    return;
}

PEDIT( pedit_create )
{
    POLL_DATA *poll, *loop;
         
    if ( argument[0] == '\0' )
    {
        send_to_char("Syntax: pedit create [topic]\n\r",ch);
        return FALSE;
    }
     
    for ( loop = poll_first; loop != NULL; loop = loop->next )
    {
        if ( !str_cmp( loop->topic, argument ) )
        {
            send_to_char("There is already a poll with that topic.\n\r", ch );
            return FALSE;
        }
    }

    poll = new_poll();
    top_poll++;
        
    if ( poll_first == NULL )
         poll_first = poll;

    for ( loop = poll_first; loop != NULL; loop = loop->next )
    {
        if ( loop == poll )
            break;

        if ( loop->next == NULL )
	{
	    loop->next = poll;
	    poll->id = loop->id + 1;
	}
    }

    free_string( poll->topic );
    poll->topic			= str_dup( argument );
    free_string( poll->creator );
    poll->creator		= str_dup( ch->name );
    poll->created		= current_time;

    ch->desc->pEdit             = (void *)poll;

    send_to_char("New Poll Created.\n\r",ch);
    return TRUE;
}

PEDIT( pedit_show )
{
    POLL_DATA *poll;
    char buf[MSL];
    int i;

    EDIT_POLL(ch,poll);

    sprintf( buf, "Topic: %s [%d]%s\n\r",
        format_output(poll->topic, 7), poll->id,
	IS_SET(poll->flags, POLL_DELETE) ? " (DEL)" : "" );
    send_to_char( buf, ch );

    sprintf( buf, "Created by: %s on %.24s MST\n\r",
	poll->creator, ctime(&poll->created) );
    send_to_char( buf, ch );

    sprintf( buf, "To: %s\n\r",
	IS_NULLSTR(poll->to) ? "(none)" : poll->to );
    send_to_char( buf, ch );

    sprintf( buf, "Poll Opens:  %.24s MST\n\r",
	ctime(&poll->open) );
    send_to_char( buf, ch );

    sprintf( buf, "Poll Closes: %.24s MST\n\r",
	ctime(&poll->close) );
    send_to_char( buf, ch );

    sprintf( buf, "Published: %s\n\r",
	IS_SET(poll->flags, POLL_PUBLISHED) ? "Yes" : "No" );
    send_to_char( buf, ch );

    send_to_char( "\n\rPoll Choices:\n\r", ch );

    for ( i = 0; i < MAX_CHOICE; i++ )
    {
	sprintf( buf, "%2d) %s\n\r",
	    (i+1), IS_NULLSTR(poll->choice[i]) ? "(none)" :
	    format_output(poll->choice[i], 4) );
	send_to_char( buf, ch );
    }

    return FALSE;
}

PEDIT( pedit_topic )
{
    POLL_DATA *poll;
    EDIT_POLL(ch,poll);

    if ( !can_edit_poll( ch, poll ) )
	return FALSE;

    if ( argument[0] == '\0' )
    {
	send_to_char( "Syntax: topic [new topic]\n\r", ch );
	return FALSE;
    }

    free_string( poll->topic );
    poll->topic = str_dup( argument );
    send_to_char( "Topic set.\n\r", ch );

    return FALSE;
}

PEDIT( pedit_to )
{
    POLL_DATA *poll;
    EDIT_POLL(ch,poll);

    if ( !can_edit_poll( ch, poll ) )
        return FALSE;

    if ( argument[0] == '\0' )
    {
	send_to_char( "PEdit: to <all|immortals|implementors>\n\r", ch );
	send_to_char( "       to clear\n\r", ch );
	return FALSE;
    }

    if ( !str_cmp( argument, "clear" ) )
    {
	free_string( poll->to );
	poll->to = str_dup( "" );
	send_to_char( "To string cleared.\n\r", ch );
	return TRUE;
    }
    else if ( str_cmp( argument, "all" )
    && str_prefix( argument, "implementors" )
    && str_prefix( argument, "immortals" ) )
    {
	send_to_char( "PEdit: Invalid TO string.\n\r", ch );
	return FALSE;
    }

    free_string( poll->to );
    poll->to = str_dup( argument );
    send_to_char( "To string set.\n\r", ch );
    return TRUE;
}

PEDIT( pedit_open )
{
    POLL_DATA *poll;
    char date[MIL];
    long raw_time = 0;

    EDIT_POLL(ch,poll);

    if ( !can_edit_poll( ch, poll ) )
        return FALSE;

    one_argument( argument, date );

    if ( date[0] == '\0' || strlen(date) < 10
    || !strstr( date, "/" ) )
    {
	send_to_char( "PEdit: open [opening date] (MM/DD/YYYY)\n\r", ch );
	return FALSE;
    }

    if ( ( raw_time = get_raw_time(date) ) <= 0 )
    {
	/* real max date is 01/18/2038 */
	send_to_char( "Invalid date. Date must be between 01/01/1970 and 01/01/2038.\n\r", ch );
	return FALSE;
    }

    poll->open = raw_time;
    send_to_char( "Open date set.\n\r", ch );

    if ( poll->close < poll->open )
    {
	poll->close = raw_time + 86400;	/* open time plus one day */
	send_to_char( "Close date updated.\n\r", ch );
    }

    return TRUE;
}

PEDIT( pedit_close )
{
    POLL_DATA *poll;
    char date[MIL];
    long raw_time = 0;
    
    EDIT_POLL(ch,poll);
    
    if ( !can_edit_poll( ch, poll ) )
        return FALSE;
 
    one_argument( argument, date );

    if ( date[0] == '\0' || strlen(date) < 10
    || !strstr( date, "/" ) )
    {
        send_to_char( "PEdit: close [close date] (MM/DD/YYYY)\n\r", ch );
        return FALSE;
    }
    
    if ( ( raw_time = get_raw_time(date) ) <= 0 )
    {
	/* real max date is 01/18/2038 */
        send_to_char( "Invalid date. Date must be between 01/01/1970 and 01/01/2038.\n\r", ch );
        return FALSE;
    }
    
    poll->close = raw_time;
    send_to_char( "Close date set.\n\r", ch );
    
    if ( poll->open > poll->close )
    {
        poll->open = raw_time - 86400; /* close time minus one day */
        send_to_char( "Open date updated.\n\r", ch );
    }

    return TRUE;
}

PEDIT( pedit_reopen )
{
    POLL_DATA *poll;  
    char date[MIL];
    long raw_time = 0;
    
    EDIT_POLL(ch,poll);
        
    one_argument( argument, date );
    
    if ( date[0] == '\0' || strlen(date) < 10 
    || !strstr( date, "/" ) )
    {
        send_to_char( "PEdit: reopen [new close date] (MM/DD/YYYY)\n\r", ch );
        return FALSE; 
    }
    
    if ( ( raw_time = get_raw_time(date) ) <= 0 )
    {
        /* real max date is 01/18/2038 */
        send_to_char( "Invalid date. Date must be between 01/01/1970 and 01/01/2038.\n\r", ch );
        return FALSE;
    }
    
    if ( raw_time < current_time || raw_time < poll->close )
    {
	send_to_char( "Invalid date. Date must be later than original close date.\n\r", ch );
	return FALSE;
    }

    poll->close = raw_time;
    send_to_char( "Close date set. Poll re-opened for voting.\n\r", ch );
    
    return TRUE;
}


PEDIT( pedit_choice )
{
    POLL_DATA *poll;
    char buf[MSL];
    char arg1[MIL];
    int num;

    EDIT_POLL(ch,poll);

    if ( !can_edit_poll( ch, poll ) )
        return FALSE;

    argument = one_argument( argument, arg1 );

    if ( arg1[0] == '\0' || !is_number(arg1) )
    {
	send_to_char( "PEdit: choice [choice #] [text]\n\r", ch );
	send_to_char( "       choice [choice #] clear\n\r", ch );
	return FALSE;
    }

    num = atoi(arg1);

    if ( num <= 0 || num > MAX_CHOICE )
    {
	send_to_char( "Invalid choice. Please use 1 - 10.\n\r", ch );
	return FALSE;
    }

    free_string( poll->choice[num-1] );

    if ( argument[0] == '\0' || !str_cmp( argument, "clear" ) )
    {
	poll->choice[num-1] = str_dup( "" );
	sprintf( buf, "Choice %d cleared.\n\r", num );
	send_to_char( buf, ch );
	return TRUE;
    }

    poll->choice[num-1] = str_dup( argument );
    sprintf( buf, "Choice %d set: \"%s\"\n\r", num, argument );
    send_to_char( buf, ch );
    return TRUE;
}

PEDIT( pedit_publish )
{
    POLL_DATA *poll;
    EDIT_POLL(ch,poll);

    if ( IS_SET(poll->flags, POLL_DELETE) )
    {
	send_to_char( "You can't published a poll that is flagged for deletion.\n\r", ch );
	return FALSE;
    }

    if ( IS_SET(poll->flags, POLL_PUBLISHED)
    && poll->open < current_time
    && get_trust(ch) < MAX_LEVEL )
    {
	send_to_char( "You can not un-publish or modify a poll"
	    " after it has opened for voting.\n\r", ch );
	return FALSE;
    }

    if ( !IS_SET(poll->flags, POLL_PUBLISHED)
    && poll->close < current_time )
    {
	send_to_char( "You can't publish a poll in the past.\n\r", ch );
	return FALSE;
    }

    if ( IS_SET(poll->flags, POLL_PUBLISHED) )
	send_to_char( "Poll is no longer published.\n\r", ch );
    else
	send_to_char( "Poll is now published.\n\r", ch );

    TOGGLE_BIT(poll->flags, POLL_PUBLISHED);
    return TRUE;
}

PEDIT( pedit_delete )
{
    POLL_DATA *poll;
    EDIT_POLL(ch,poll);
    
    if ( IS_SET(poll->flags, POLL_PUBLISHED) )
    {
        send_to_char( "You can not delete a published poll.\n\r", ch );
        return FALSE;
    }
 
    if ( IS_SET(poll->flags, POLL_DELETE) )
        send_to_char( "Poll is no longer flagged for deletion.\n\r", ch );
    else
        send_to_char( "Poll is now flagged for deletion.\n\r", ch );
    
    TOGGLE_BIT(poll->flags, POLL_DELETE);
    return TRUE;
}

PEDIT( pedit_list )
{
    POLL_DATA *loop;
    char buf[MSL];
    int count = 0;

    for ( loop = poll_first; loop != NULL; loop = loop->next )
    {
	sprintf( buf, "%3d) %s%s\n\r", ++count,
	    format_output(loop->topic, 5),
	    IS_SET(loop->flags, POLL_DELETE) ? " (DEL)" : "" );
	send_to_char( buf, ch );
    }

    if ( count > 0 )
	sprintf( buf, "\n\rTotal Polls: %d\n\r", count );
    else
	sprintf( buf, "There are currently no polls defined.\n\r" );
    send_to_char( buf, ch );

    return FALSE;
}

PEDIT( pedit_credits )
{
    send_to_char( POLL_CREDITS, ch );
    return FALSE;
}

/*
 * get_poll_data - gets the poll requested by the player
 * retrieves the data out of the list of the currently loaded
 * polls. The 'num' passed here is NOT the unique id, it is
 * the number of the poll as it is loaded in memory.
 */
POLL_DATA *get_poll_data( int num )
{
    POLL_DATA *poll;
    int count = 0;

    for ( poll = poll_first; poll != NULL; poll = poll->next )
    {
	if ( ++count == num )
	    return poll;
    }

    return NULL;
}

/*
 * can_edit_poll - checks to see if the poll is allowed
 * to be edited. Published, flagged for deletion, and
 * polls that have been voted on may NOT be changed.
 */
bool can_edit_poll( CHAR_DATA *ch, POLL_DATA *poll )
{
    if ( IS_SET(poll->flags, POLL_DELETE) )
    {
        send_to_char( "You can't change a poll that is flagged for deletion.\n\r", ch );
        return FALSE;
    }
    else if ( IS_SET(poll->flags, POLL_PUBLISHED) )
    {
	send_to_char( "You can't change a poll that has been published.\n\r", ch );
	return FALSE;
    }
    else
    {
	int i;

	for ( i = 0; i < MAX_CHOICE; i++ )
	{
	    if ( poll->votes[i] > 0 )
	    {
		send_to_char( "You can't change a poll that has been voted on.\n\r", ch );
		return FALSE;
	    }
	}
    }

    return TRUE;
}

/*
 * get_raw_time - date conversion function.
 * converts MM/DD/YYYY to a 'struct tm' format,
 * then to a long integer for storing.
 */
long get_raw_time( char *date )
{
    char num[MIL] = { '\0' };
    char *ptr;
    int i;
    struct tm time;
    
    ptr = date; 
    i = 0;
    
    for ( ; *ptr != '/'; ptr++ )
        num[i++] = *ptr;
        
    time.tm_mon = atoi(num) - 1;
    ptr++;
    i = 0;
     
    for ( ; *ptr != '/'; ptr++ )
        num[i++] = *ptr;
     
    time.tm_mday = atoi(num);
    ptr++;
    i = 0;
    
    for ( ; *ptr != '\0'; ptr++ )
        num[i++] = *ptr;
        
    time.tm_year = atoi(num) - 1900;
    
    time.tm_sec = 0;
    time.tm_min = 0;
    time.tm_hour = 0;
    time.tm_isdst = 0;

    return mktime( &time );
}

/*
 * has_voted - checks the pfile to see if the unique id
 * has already been voted on. the '|' used in this string
 * is NOT a bitwise function. it is used as a delimiter,
 * much like a comma.
 */
bool has_voted( CHAR_DATA *ch, POLL_DATA *poll )
{
    char id[MIL] = { '\0' };
    char *list;		// list of polls the character has voted on
    int i = 0;

    if ( poll->open > current_time || poll->close < current_time )
	return TRUE;

    for ( list = ch->pcdata->polls; *list != '\0'; list++ )
    {
	if ( *list == '|' )
	{
	    id[0] = '\0';
	    i = 0;
	    continue;
	}

	id[i++] = *list;

	if ( *(list+1) == '|' || *(list+1) == '\0' )
	{
	    if ( atoi(id) == poll->id )
		return TRUE;
	}
    }

    return FALSE;
}

/*
 * days_left - computes the numbers of days left
 * for players to vote on a specific poll. This
 * is used soley for display purposes.
 */
int days_left( POLL_DATA *poll )
{
    int days;
    int one_day = 86400;

    if ( poll == NULL || current_time > poll->close )
	return 0;

    days = ( poll->close - current_time ) / one_day;

    return UMAX(1, days);
}

/*
 * poll_compact - used to remove the 'blank' choices
 * from a poll to ensure no errors occur in saving
 * or loading. called in save_polls.
 */
void poll_compact( POLL_DATA *poll )
{
    int i;
    bool found = TRUE;

    while ( found )
    {
	found = FALSE;

        for ( i = 1; i < MAX_CHOICE; i++ )
        {
	    if ( !IS_NULLSTR(poll->choice[i])
	    && IS_NULLSTR(poll->choice[i-1]) )
	    {
		free_string(poll->choice[i-1]);
		poll->choice[i-1] = str_dup(poll->choice[i]);
		free_string(poll->choice[i]);
		poll->choice[i] = str_dup( "" );
		found = TRUE;
	    }
	}
    }

    return;
}

/*
 * format_output - output string formatter
 * used to format a string before it is placed into
 * a 'buf' for output to a character. the 'spaces' is
 * the number of characters that should be reserved
 * for the original text.
 * ie: "Topic: " is 7 characters.
 * syntax: format_output( string, 7 )
 */
char *format_output( char *string, int spaces )
{
    char buf[MSL] = { '\0' };
    char spc[MSL];
    char *ptr;                  // temp string for looping
    char *output = '\0';
    bool hyphen = FALSE;        // TRUE means breaking in the middle of a word, need a hyphen
    bool first = TRUE;          // first time adding to buf, FALSE means add 'spaces'
    int strLength;              // max characters before line wrap
    int pos = 0;                // position in string
    int loc, i;                 // location of last space and counter

    sprintf( spc, "\n\r%*c", spaces, ' ' );
    ptr = string;

    /* keep looping through until the enter string is copied */
    while ( *ptr != '\0' )
    {
        /* set goalLength to maximum acceptable line length */
        strLength = 74 - spaces;
    
        /* reset tracking */
        loc = 0;
        hyphen = FALSE;
    
        /* loop through up to goalLength characters, or until
         * there are no characters left in the string
         */
        for ( i = 0; i < strLength; i++ )
        {
            /* skip spaces at the beginning of a line */
            if ( i == 0 && *ptr == ' ' )
            {
                i--;
                ptr++;
                continue;
            }
        
/* section commented. color code parsing.
 * section can be re-enabled if modified
 * mud specific color codes, such as '^'
 * or '{'. 

            // ignore colour codes
            if ( *(ptr+i) == '^' )
            {
                strLength++;
           
                /* make sure it's actually a colour code, not an actual ^ */
                if ( *(ptr+i+1) != '^' )
                    strLength++;
            }
*/
             
            /* keep track of where the last space is */
            else if ( *(ptr+i) == ' ' )
                loc = i; 
             
            /* check for end of string */
            else if ( *(ptr+i) == '\0' )
            {
                loc = i;
                break;
            }
        }
                
        /* no spaces in strLength characters?! */
        if ( loc == 0 && *(ptr+i) != '\0' )
        {    
            /* mark for a hyphen */
            loc = strLength - 2;
            hyphen = TRUE;
        }    
            
        /* add begining spaces */
        if ( first )
            first = FALSE;
        else
        {
            strcat( buf, spc );
            pos += spaces + 2;
        }
        
        /* copy up to the last space (or newline) into the buffer */
        strncpy( &buf[pos], ptr, loc );
            
        /* move the position ahead by the number of chars added */
        pos += loc;
            
        /* move the pointer ahead */
        ptr += loc; 
            
        /* Add the line break */
        if ( hyphen )          // break in the middle of a line
            buf[pos++] = '-';  // add a hyphen
    }
         
    output = str_dup(buf);
    return (output);
}