/***************************************************************************
* _/ _/ *
* _/_/_/ _/_/ _/_/_/ _/ _/_/ _/ _/ _/_/_/ *
* _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ *
* _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ *
* _/ _/ _/ _/_/_/ _/ _/_/ _/_/_/ _/_/_/ *
***************************************************************************
* Mindcloud Copyright 2001-2003 by Jeff Boschee (Zarius), *
* Additional credits are in the help file CODECREDITS *
* All Rights Reserved. *
***************************************************************************/
/*
* Original code by Xkilla
* Cleaned up by Dreimas
*/
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdarg.h>
#include "merc.h"
#include "changes.h"
/*
* Globals
*/
char *current_date args( ( void ) );
/*
* Local Functions
*/
int maxChanges;
#define NULLSTR( str ) ( str == NULL || str[0] == '\0' )
CHANGE_DATA *changes_table;
void do_echo( CHAR_DATA * ch, char *argument );
void load_changes( void )
{
FILE *fp;
int i;
if( !( fp = fopen( CHANGES_FILE, "r" ) ) )
{
bug( "Could not open changes.dat file for reading.", 0 );
return;
}
fscanf( fp, "%d\n", &maxChanges );
/*
* Use malloc so we can realloc later on
*/
changes_table = ( CHANGE_DATA * ) malloc( sizeof( CHANGE_DATA ) * ( maxChanges + 1 ) );
for( i = 0; i < maxChanges; i++ )
{
changes_table[i].change = fread_string( fp );
changes_table[i].coder = fread_string( fp );
changes_table[i].date = fread_string( fp );
changes_table[i].type = fread_string( fp );
changes_table[i].mudtime = fread_number( fp );
}
changes_table[maxChanges].coder = str_dup( "" );
fclose( fp );
return; /* just return */
}
char *current_date( )
{
static char buf[128];
struct tm *datetime;
datetime = localtime( ¤t_time );
strftime( buf, sizeof( buf ), "%m/%d/%Y", datetime );
return buf;
}
void save_changes( void )
{
FILE *fp;
int i;
if( !( fp = fopen( CHANGES_FILE, "w" ) ) )
{
perror( CHANGES_FILE );
return;
}
fprintf( fp, "%d\n", maxChanges );
for( i = 0; i < maxChanges; i++ )
{
fprintf( fp, "%s~\n", changes_table[i].change );
fprintf( fp, "%s~\n", changes_table[i].coder );
fprintf( fp, "%s~\n", changes_table[i].date );
fprintf( fp, "%s~\n", changes_table[i].type );
fprintf( fp, "%ld\n", changes_table[i].mudtime );
fprintf( fp, "\n" );
}
fclose( fp );
return;
}
void delete_change( int iChange )
{
int i, j;
CHANGE_DATA *new_table;
new_table = ( CHANGE_DATA * ) malloc( sizeof( CHANGE_DATA ) * maxChanges );
if( !new_table )
{
return;
}
for( i = 0, j = 0; i < maxChanges + 1; i++ )
{
if( i != iChange )
{
new_table[j] = changes_table[i];
j++;
}
}
free( changes_table );
changes_table = new_table;
maxChanges--;
return;
}
void do_addchange( CHAR_DATA * ch, char *argument )
{
CHANGE_DATA *new_table;
char arg1[MAX_INPUT_LENGTH];
char buf[MSL];
argument = one_argument( argument, arg1 );
if( IS_NPC( ch ) )
return;
if( argument[0] == '\0' )
{
send_to_char( "Syntax: Addchange <type> <string>\n\r", ch );
send_to_char( "#wTypes are: code, area, help, rule, typo.#n\n\r", ch );
send_to_char( "#wType '#Rchanges#w' to view the list.#n\n\r", ch );
return;
}
/*
* Addchange must have an argument now - Zarius
*/
if( str_cmp( arg1, "code" ) && str_cmp( arg1, "area" ) && str_cmp( arg1, "help" ) && str_cmp( arg1, "rule" )
&& str_cmp( arg1, "typo" ) )
{
send_to_char( "Incorrect Type! Must be code, area, help, rule or typo ONLY\n\r", ch );
return;
}
if( strlen( argument ) < 10 )
{
send_to_char( "The change description must be longer than 10 chars.\n\r", ch );
return;
}
maxChanges++;
new_table = ( CHANGE_DATA * ) realloc( changes_table, sizeof( CHANGE_DATA ) * ( maxChanges + 1 ) );
if( !new_table ) /* realloc failed */
{
send_to_char( "Memory allocation failed. Brace for impact.\n\r", ch );
return;
}
changes_table = new_table;
changes_table[maxChanges - 1].change = str_dup( argument );
changes_table[maxChanges - 1].coder = str_dup( ch->name );
changes_table[maxChanges - 1].date = str_dup( current_date( ) );
changes_table[maxChanges - 1].type = str_dup( capitalize( arg1 ) );
changes_table[maxChanges - 1].mudtime = current_time;
send_to_char( "Changes Created.\n\r", ch );
send_to_char( "Type 'changes' to see the changes.\n\r", ch );
xprintf( buf, "#D<#CCHANGE#D> #w%s change #R##%d #wadded, type '#Rchanges#w' to see the details.#D\n\r",
capitalize( arg1 ), maxChanges );
do_echo( ch, buf );
save_changes( );
return;
}
void do_delchange( CHAR_DATA * ch, char *argument )
{
char arg1[MAX_INPUT_LENGTH];
char buf[MSL];
int num;
argument = one_argument( argument, arg1 );
if( IS_NPC( ch ) )
return;
if( !ch->desc || NULLSTR( arg1 ) || !is_number( arg1 ) )
{
send_to_char( "#wFor delchange you must provide a change number.#D\n\r", ch );
send_to_char( "Syntax: delchange (change number)\n\r", ch );
return;
}
num = atoi( arg1 );
if( num < 0 || num > maxChanges )
{
xprintf( buf, "Valid changes are from 0 to %d.\n\r", maxChanges );
send_to_char( buf, ch );
return;
}
delete_change( num );
send_to_char( "Change deleted.\n\r", ch );
return;
}
/*
* The following format code has been adapted from KaViR's justify
* snippet -- Dreimas
*/
static void AddSpaces( char **ppszText, int iNumber )
{
int iLoop;
for( iLoop = 0; iLoop < iNumber; iLoop++ )
{
*( *ppszText )++ = ' ';
}
}
char *change_justify( char *pszText, int iAlignment )
{
static char s_szResult[4096];
char *pszResult = &s_szResult[0];
char szStore[4096];
int iMax;
int iLength = iAlignment - 1;
int iLoop = 0;
if( strlen( pszText ) < 10 )
{
strcpy( s_szResult, "BUG: Justified string cannot be less than 10 characters long." );
return ( &s_szResult[0] );
}
while( *pszText == ' ' )
pszText++;
szStore[iLoop++] = *pszText++;
if( szStore[iLoop - 1] >= 'a' && szStore[iLoop - 1] <= 'z' )
szStore[iLoop - 1] = UPPER( szStore[iLoop - 1] );
/*
* The first loop goes through the string, copying it into szStore. The
* * string is formatted to remove all newlines, capitalise new sentences,
* * remove excess white spaces and ensure that full stops, commas and
* * exclaimation marks are all followed by two white spaces.
*/
while( *pszText )
{
switch ( *pszText )
{
default:
szStore[iLoop++] = *pszText++;
break;
case ' ':
if( *( pszText + 1 ) != ' ' )
{
/*
* Store the character
*/
szStore[iLoop++] = *pszText;
}
pszText++;
break;
case '.':
case '?':
case '!':
szStore[iLoop++] = *pszText++;
switch ( *pszText )
{
default:
szStore[iLoop++] = ' ';
szStore[iLoop++] = ' ';
/*
* Discard all leading spaces
*/
while( *pszText == ' ' )
pszText++;
/*
* Store the character
*/
szStore[iLoop++] = *pszText++;
if( szStore[iLoop - 1] >= 'a' && szStore[iLoop - 1] <= 'z' )
szStore[iLoop - 1] &= ~32;
break;
case '.':
case '?':
case '!':
break;
}
break;
case ',':
/*
* Store the character
*/
szStore[iLoop++] = *pszText++;
/*
* Discard all leading spaces
*/
while( *pszText == ' ' )
pszText++;
/*
* Commas shall be followed by one space
*/
szStore[iLoop++] = ' ';
break;
case '$':
szStore[iLoop++] = *pszText++;
while( *pszText == ' ' )
pszText++;
break;
case '\n':
case '\r':
pszText++;
break;
}
}
szStore[iLoop] = '\0';
/*
* Initialise iMax to the size of szStore
*/
iMax = strlen( szStore );
/*
* The second loop goes through the string, inserting newlines at every
* * appropriate point.
*/
while( iLength < iMax )
{
/*
* Go backwards through the current line searching for a space
*/
while( szStore[iLength] != ' ' && iLength > 1 )
iLength--;
if( szStore[iLength] == ' ' )
{
szStore[iLength] = '\n';
iLength += iAlignment;
}
else
break;
}
/*
* Reset the counter
*/
iLoop = 0;
/*
* The third and final loop goes through the string, making sure that there
* * is a \r (return to beginning of line) following every newline, with no
* * white spaces at the beginning of a particular line of text.
*/
while( iLoop < iMax )
{
/*
* Store the character
*/
*pszResult++ = szStore[iLoop];
switch ( szStore[iLoop] )
{
default:
break;
case '\n':
*pszResult++ = '\r';
while( szStore[iLoop + 1] == ' ' )
iLoop++;
/*
* Add spaces to the front of the line as appropriate
*/
AddSpaces( &pszResult, 25 );
break;
}
iLoop++;
}
*pszResult++ = '\0';
return ( &s_szResult[0] );
}
int num_changes( void )
{
char *test;
int today;
int i;
i = 0;
test = current_date( );
today = 0;
for( i = 0; i < maxChanges; i++ )
if( !str_cmp( test, changes_table[i].date ) )
today++;
return today;
}
void do_changes( CHAR_DATA * ch, char *argument )
{
char arg[MIL];
char buf[MSL];
char *test;
int today;
int i;
bool fAll;
int totalpages = 1;
one_argument( argument, arg );
if( IS_NPC( ch ) )
return;
if( str_cmp( arg, "code" ) && str_cmp( arg, "area" ) && str_cmp( arg, "help" ) && str_cmp( arg, "rule" )
&& str_cmp( arg, "typo" ) && str_cmp( arg, "all" ) && !NULLSTR( arg ) )
{
send_to_char( "Incorrect Type! Must be code, area, help, rule, type or ALL\n\r", ch );
return;
}
if( maxChanges < 1 )
return;
i = 0;
test = current_date( );
today = 0;
for( i = 0; i < maxChanges; i++ )
if( !str_cmp( test, changes_table[i].date ) )
today++;
if( NULLSTR( arg ) )
fAll = FALSE;
else
fAll = TRUE;
send_to_char( "\n\r", ch );
xprintf( buf, "#R-=[ #W%s Changelog #R]=-", MUDNAME );
cent_to_char( buf, ch );
pager_to_char( "#R--------------------------------------------------------------------------------#n\n\r", ch );
pager_to_char( "#wNo. Coder Date Type Change#n\n\r", ch );
pager_to_char( "#R--------------------------------------------------------------------------------#n\n\r", ch );
if( is_number( arg ) )
{
int page = atoi( arg );
int number;
number = page * 10;
if( page < 0 || page > totalpages )
{
ch_printf( ch, "#RPage must be between 1 and %d!!!\n\r", totalpages );
return;
}
for( i = ( number - 10 ); ( i < number && i < maxChanges ); i++ )
{
pager_printf( ch, "#0[#R%3d#0] %-11s #c*%-6s #P%-5s #w%-45s#n\n\r", ( i + 1 ), changes_table[i].coder,
changes_table[i].date, changes_table[i].type, change_justify( changes_table[i].change, 45 ) );
}
}
else {
for( i = 0; i < maxChanges; i++ )
{
if( !fAll && changes_table[i].mudtime + ( 7 * 24L * 3600L ) < current_time )
continue;
if( !str_cmp( arg, "code" ) && str_cmp( changes_table[i].type, "code" ) )
continue;
if( !str_cmp( arg, "area" ) && str_cmp( changes_table[i].type, "area" ) )
continue;
if( !str_cmp( arg, "help" ) && str_cmp( changes_table[i].type, "help" ) )
continue;
if( !str_cmp( arg, "rule" ) && str_cmp( changes_table[i].type, "rule" ) )
continue;
if( !str_cmp( arg, "typo" ) && str_cmp( changes_table[i].type, "typo" ) )
continue;
pager_printf( ch, "#0[#R%3d#0] %-11s #c*%-6s #P%-5s #w%-55s#D\n\r", ( i + 1 ), changes_table[i].coder,
changes_table[i].date, changes_table[i].type, change_justify( changes_table[i].change, 55 ) );
}
}
pager_to_char( "#R--------------------------------------------------------------------------------#n\n\r", ch );
pager_printf( ch, "#w There are #D[ #Y%d#D ] #wchanges in the database, #Y%d #wof them were added today.#n\n\r",
maxChanges, today );
pager_to_char( "#0 Also see: '#Cchanges all#0' for a list of all the changes.#0\n\r", ch );
pager_to_char( "#0 Or: '#Cchanges <type>#0' for a filtered list of all.#n\n\r", ch );
if( totalpages > 1 )
{
pager_printf( ch, "#0 Or: '#Cchanges <1 to %d>#0' for individual pages.#n\n\r", totalpages );
}
pager_to_char( "#R--------------------------------------------------------------------------------#n\n\r", ch );
return;
}