/*************************************************************************** * _/ _/ * * _/_/_/ _/_/ _/_/_/ _/ _/_/ _/ _/ _/_/_/ * * _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ * * _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ * * _/ _/ _/ _/_/_/ _/ _/_/ _/_/_/ _/_/_/ * *************************************************************************** * 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; }