Well everyone, I tried to send this email out last night, but it got
trapped by [EMAIL PROTECTED], saying it had a suspicious header, but now
that my emails seem to be working correctly I'll try sending it out
again :D it's been literally years since I've given back to the coding
community, and I've taken my fair share, so here's an immortal utility I
wrote for my mud over the weekend... there may still be a memory leak or
2, but I've tried to nail everything I could, just report any errors to
me that you may find and I'll get em fixed :D Also, sorry for the lack
of a help file, but I'm lazy about that, so I'll just post some sample
usages of the command and you can take it from there... it's called
PList, and lets you get a listing from your player directory of all
pfiles fitting a certain criteria, and even sort them based on one of
those criteria... examples include:
plist
plist clan tinker
plist level > 30 level < 50 race human sort level
plist laston < 7 level > 100 clan admin sort laston
plist name v clan coder laston > 10 laston < 30 sort level
etc, etc... I'm sure you get the idea :D hope you enjoy it :) I've found
it helpful for spotting newbies if I wasn't able to be on when they
created, just so they don't feel like the mud is abandoned (since we're
still getting things up and running)...
Richard Lindsey
p.s. I'm going to insert the test of it all below, I had originally sent
it as an attachment, but that may be why rom-admin stopped it for a
funky header...
<----------------------------Code begins
below----------------------------->
/*
* Installation should be simple... Create the command entry in
interp.c,
* add a DECLARE_DO_FUN entry to interp.h, and then just copy the code
* below into the bottom of your act_wiz.c file, should be a clean
compile
* but i make no promises and don't guarantee this code in any way...
i'm
* fairly certain there should be no drastic-type problems with it, but
* this is the cover-my-ass statement that's so common in most snippets
:D
* I make no guarantee that this code won't screw your mud, and am in no
* way responsible should this happen, you are installing this code at
* your own risk and accept any consequences as your own problems...
Now,
* having said that, i can try to help troubleshoot any problems you may
* have installing this, if you email me at velveeta_512#yahoo.com (just
* replace the # with an @), or you can drop by my test port for Lace of
* Ages at loa.slayn.net 9001... also, please report any bugs and/or you
* find with this to me so that i can make the fixes on my end... There
* might be a memory leak or 2 left in this thing, so if you find
anything
* just let me know :) Also, my codebase has been altered thus that some
* of the structures referenced below are no longer in mine, so i wrote
it
* and debugged it until it worked on mine, and then altered it so that
it
* should work on a stock codebase, and haven't tested it after making
* those additions...
*
* In conclusion, all I ask is that you retain the commented header line
* below when you place it into your code, you don't even have to email
* me and let me know if you're using it (though if you want to that's
fine
* with me :D)... and please send me those bug reports!
*/
/***********************************************************************
*******************
* PList, written by Velveeta for Lace of Ages MUD: 05/07/2004
*
* Used to get a listing of existent player files meeting certain
*
* criteria specified by the user, and sort it in a variety of ways.
*
*
*
* Sample usage: plist
*
* plist race human clan tinker class mage level > 30
laston < 7 sort level *
************************************************************************
******************/
#define BYNAME 0
#define BYLEVEL 1
#define BYRACE 2
#define BYCLAN 3
#define BYCLASS 4
#define BYLASTON 5
typedef struct compare_data COMPARE_DATA;
typedef struct pfile_data PFILE_DATA;
typedef struct search_data SEARCH_DATA;
struct compare_data
{
COMPARE_DATA * next;
char * name;
char operator;
int value;
bool valid;
};
struct pfile_data
{
PFILE_DATA * next;
char * name;
sh_int level;
sh_int race;
sh_int clan;
sh_int class;
sh_int sex;
long laston;
bool valid;
};
struct search_data
{
char * name;
sh_int race;
sh_int clan;
sh_int class;
sh_int sort;
bool all;
COMPARE_DATA * comparison;
};
COMPARE_DATA *compare_free;
COMPARE_DATA *new_compare( void )
{
static COMPARE_DATA compare_zero;
COMPARE_DATA *compare;
if ( compare_free == NULL )
compare = alloc_perm( sizeof( *compare ) );
else
{
compare = compare_free;
compare_free = compare_free->next;
}
*compare = compare_zero;
VALIDATE(compare);
return compare;
}
void free_compare( COMPARE_DATA *compare )
{
if ( !IS_VALID(compare) )
return;
if ( compare->name )
free_string(compare->name);
INVALIDATE(compare);
compare->next = compare_free;
compare_free = compare;
}
PFILE_DATA *pfile_free;
PFILE_DATA *new_pfile( void )
{
static PFILE_DATA pfile_zero;
PFILE_DATA *pfile;
if ( pfile_free == NULL )
pfile = alloc_perm( sizeof( *pfile ) );
else
{
pfile = pfile_free;
pfile_free = pfile_free->next;
}
*pfile = pfile_zero;
VALIDATE(pfile);
return pfile;
}
void free_pfile( PFILE_DATA *pfile )
{
if ( !IS_VALID(pfile) )
return;
if ( pfile->name )
free_string(pfile->name);
INVALIDATE(pfile);
pfile->next = pfile_free;
pfile_free = pfile;
}
PFILE_DATA *pfile_matches;
void display_plist_output( CHAR_DATA *ch, int count )
{
BUFFER *output;
char buf[MAX_STRING_LENGTH], race[MAX_STRING_LENGTH];
char class[MAX_STRING_LENGTH], laston[MAX_STRING_LENGTH];
int x;
/*
* This function displays the total output of the list of all found
* matches, this is the only place you'll need to add color codes or
* do any new formatting and alignment if you wish to add any new
* fields...
*/
output = new_buf();
sprintf(buf, " Name Level Race Class Clan
Laston\n\r");
add_buf(output,buf);
sprintf(buf,
"_______________________________________________________________________
________\n\r");
add_buf(output,buf);
sprintf(buf, "| | | | | |
|\n\r");
add_buf(output,buf);
for ( x = 0; x < count; x++ )
{
sprintf(race, "%-5.5s", pfile_matches[x].race >= 0 ?
capitalize(race_table[pfile_matches[x].race].name) : "
");
sprintf(class, "%-5.5s", pfile_matches[x].class >= 0 ?
capitalize(race_table[pfile_matches[x].class].name) : "
");
strftime(laston, MAX_STRING_LENGTH - 1, "%a %b %e %H:%M:%S",
ctime(current_time));
sprintf(buf, "| %-12s | %3d | %s | %s | %-14.14s | %-19.19s
|\n\r",
pfile_matches[x].name, pfile_matches[x].level, race, class,
pfile_matches[x].clan > 0 ?
capitalize(clan_table[pfile_matches[x].clan) :
" ", laston );
add_buf(output,buf);
}
sprintf(buf, "| | | | | |
| |\n\r");
add_buf(output,buf);
sprintf(buf, "%d players found.\n\r", count);
add_buf(output,buf);
page_to_char(buf_string(output),ch);
free_buf(output);
}
/* Privates to the sort_plist_matches function for qsort */
static int plist_name(const void *v1, const void *v2)
{
PFILE_DATA *i1 = (PFILE_DATA *) v1, *i2 = (PFILE_DATA *) v2;
return strcmp(i1->name, i2->name);
}
static int plist_level(const void *v1, const void *v2)
{
PFILE_DATA *i1 = (PFILE_DATA *) v1, *i2 = (PFILE_DATA *) v2;
return i1->level < i2->level ? -1 : (i1->level == i2->level ? 0 : 1
);
}
static int plist_race(const void *v1, const void *v2)
{
PFILE_DATA *i1 = (PFILE_DATA *) v1, *i2 = (PFILE_DATA *) v2;
return strcmp(race_table[i1->race].name, race_table[i2->race].name);
}
static int plist_class(const void *v1, const void *v2)
{
PFILE_DATA *i1 = (PFILE_DATA *) v1, *i2 = (PFILE_DATA *) v2;
return strcmp(class_table[i1->class].name,
class_table[i2->class].name);
}
static int plist_clan(const void *v1, const void *v2)
{
PFILE_DATA *i1 = (PFILE_DATA *) v1, *i2 = (PFILE_DATA *) v2;
return strcmp(clan_table[UMAX(0,i1->clan)].name,
clan_table[UMAX(0,i2->clan)].name);
}
static int plist_laston(const void *v1, const void *v2)
{
PFILE_DATA *i1 = (PFILE_DATA *) v1, *i2 = (PFILE_DATA *) v2;
return i1->laston > i2->laston ? -1 : ( i1->laston == i2->laston ? 0
: 1 );
}
void sort_plist_matches( int count, int sort )
{
/*
* Any new fields you wish to sort by should have a value defined
* above, assigned below in get_pfile_matches, and then checked for
* here, with a sorting function created above, you should be able
* to figure out how to add any new sorting functions by looking at
* all the other ones that are already created...
*/
switch( sort )
{
case BYNAME:
qsort( (void *) pfile_matches, count, sizeof(PFILE_DATA),
plist_name );
break;
case BYLEVEL:
qsort( (void *) pfile_matches, count, sizeof(PFILE_DATA),
plist_level );
break;
case BYRACE:
qsort( (void *) pfile_matches, count, sizeof(PFILE_DATA),
plist_race );
break;
case BYCLAN:
qsort( (void *) pfile_matches, count, sizeof(PFILE_DATA),
plist_clan );
break;
case BYCLASS:
qsort( (void *) pfile_matches, count, sizeof(PFILE_DATA),
plist_class );
break;
case BYLASTON:
qsort( (void *) pfile_matches, count, sizeof(PFILE_DATA),
plist_laston );
break;
}
}
int get_plist_matches( SEARCH_DATA *criteria )
{
int count = 0;
PFILE_DATA *match;
DESCRIPTOR_DATA *d;
char *word, buf[MSL];
bool matchok, end = FALSE;
DIR *player_dir;
struct dirent *pfile;
FILE *fp;
/*
* This function is sort of a slimmed-down version of fread_char,
* intended to read in values to display in the output of the list
* of matches... You may notice the new_pfile() function called
* below, this was to eliminate a bug i was having with the mud
* crashing randomly when checking match->name to see if it should
* be freed before reassignment... it was given memory space with
* a call to malloc, now it's given a recycling function just like
* those you'd find in recycle.c... Any new fields you'd like
* displayed should be read in here and assigned to a new field
* that you add to the pfile data structure... i initialized all
* numeric values to -2 since some lookup functions return -1 if
* the value isn't found, and -2 can be used to check if a value
* just wasn't read in period...
*/
match = new_pfile();
match->name = str_dup( "" );
match->level = -2;
match->race = -2;
match->clan = -2;
match->class = -2;
match->laston = -2;
match->sex = -2;
sprintf(buf, "%s", PLAYER_DIR);
player_dir = opendir(buf);
while( ( pfile = readdir(player_dir) ) != NULL )
{
if ( pfile->d_name[0] == '.' )
continue;
strcat(buf,pfile->d_name);
if ( ( fp = file_open( buf,"r" ) ) != NULL )
{
/* First, let's initialize match */
matchok = criteria->all;
if ( !IS_NULLSTR(match->name) )
free_string( match->name );
match->name = str_dup( "" );
match->level = -2;
match->race = -2;
match->clan = -2;
match->class = -2;
match->laston = -2;
match->sex = -2;
for ( ; !end; )
{
word = feof( fp ) ? "End" : fread_word( fp );
switch( UPPER(word[0]) )
{
case '*':
fread_to_eol( fp );
break;
case 'C':
if ( !str_cmp( word, "Clan" ) )
{
match->clan = clan_lookup( fread_string( fp
) );
break;
}
if ( !str_cmp( word, "Cla" ) || !str_cmp( word,
"Class" ) )
{
match->clan = fread_number( fp );
break;
}
break;
case 'E':
if ( !str_cmp( word, "End" ) )
{
file_close(fp);
sprintf(buf,"%s",PLAYER_DIR);
end = TRUE;
break;
}
break;
case 'L':
if ( !str_cmp( word, "Lev" )
|| !str_cmp( word, "Levl" )
|| !str_cmp( word, "Level" ) )
{
match->level = fread_number( fp );
break;
}
if ( !str_cmp( word, "LogO" ) )
{
match->laston = fread_number( fp );
break;
}
break;
case 'N':
if ( !str_cmp( word, "Name" ) )
{
match->name = fread_string( fp );
break;
}
break;
case 'R':
if ( !str_cmp( word, "Race" ) )
{
match->race = race_lookup( fread_string( fp
) );
break;
}
break;
case 'S':
if ( !str_cmp( word, "Sex" ) )
{
match->sex = fread_number( fp );
break;
}
break;
}
}
end = FALSE;
for ( d = descriptor_list; d != NULL; d = d->next )
{
if ( CH(d) && !str_cmp( CH(d)->name, match->name ) ) /*
Quick check to see if they're logged on */
{
match->laston = current_time;
break;
}
}
if ( !matchok )
{
COMPARE_DATA *comparison_new;
if ( ( IS_NULLSTR(criteria->name) ||
!str_prefix(criteria->name, match->name) )
&& ( criteria->race == -2 || criteria->race ==
match->race )
&& ( criteria->class == -2 || criteria->class ==
match->class )
&& ( criteria->clan == -2 || criteria->clan ==
match->clan ) )
matchok = TRUE;
for ( comparison_new = criteria->comparison;
comparison_new != NULL; comparison_new = comparison_new->next )
{
if ( !str_prefix( comparison_new->name, "laston" ) )
{
switch( comparison_new->operator )
{
case '>':
if ( current_time - match->laston <= (
comparison_new->value + 1 ) * 86400 )
matchok = FALSE;
break;
case '=':
if ( current_time - match->laston <
comparison_new->value * 86400
|| current_time - match->laston >= (
comparison_new->value + 1 ) * 86400 )
matchok = FALSE;
break;
case '<':
if ( current_time - match->laston >=
comparison_new->value * 86400 )
matchok = FALSE;
break;
}
}
else if ( !str_prefix( comparison_new->name, "level"
) )
{
switch( comparison_new->operator )
{
case '>':
if ( match->level <=
comparison_new->value )
matchok = FALSE;
break;
case '=':
if ( match->level <
comparison_new->value || match->level > comparison_new->value )
matchok = FALSE;
break;
case '<':
if ( match->level >=
comparison_new->value )
matchok = FALSE;
break;
}
}
}
}
if ( matchok )
{
if ( !count )
pfile_matches = malloc( sizeof( PFILE_DATA ) ); /*
Use malloc so we can realloc as needed */
else
{
PFILE_DATA *new_table;
new_table = realloc( pfile_matches, sizeof(
PFILE_DATA ) * ( count + 1 ) );
if ( !new_table ) /* Reallocate failed */
{
bug("Realloc failed in get_plist_matches.", 0);
return 0;
}
pfile_matches = new_table;
}
pfile_matches[count].name = str_dup( match->name );
pfile_matches[count].level = match->level;
pfile_matches[count].race = match->race;
pfile_matches[count].clan = match->clan;
pfile_matches[count].class = match->class;
pfile_matches[count].laston = match->laston;
pfile_matches[count].sex = match->sex;
count++;
}
}
}
free_pfile(match);
return count;
}
bool get_plist_criteria( CHAR_DATA *ch, char *argument, SEARCH_DATA
*criteria )
{
char arg[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
bool correct_entry = TRUE, sorted = FALSE;
/* Parse out all options on the command line */
criteria->all = TRUE;
if ( !IS_NULLSTR(argument) ) /* If argument is NULL, show all
matches */
{
argument = one_argument( argument, arg );
/*
* For regular arguments like clan, name, class, etc. we only
* accept 1 entry, for things like level and laston, we can take
* a range of values, like level < 30 level > 10... if you'd
like
* to be able to specify more than 1 name, clan, etc. you can
use
* the comparison linked list for them, and exclude the operator
* field, just use name and value, and check later on with a
* boolean variable to see if it finds a valid name prefix in
* the list... just a thought :D
*/
for ( ; !IS_NULLSTR(arg); argument = one_argument(argument,arg)
)
{
if ( !str_prefix( arg, "clan" ) )
{
argument = one_argument(argument, arg);
criteria->all = FALSE;
if ( criteria->clan != -2 )
{
send_to_char("Duplicate entries for clan.\n\r", ch);
correct_entry = FALSE;
break;;
}
if ( ( criteria->clan = clan_lookup(arg) ) == -1 )
{
sprintf(buf, "Invalid clan name: \"%s\"\n\r", arg);
send_to_char(buf,ch);
correct_entry = FALSE;
break;
}
continue;
}
if ( !str_prefix( arg, "class" ) )
{
argument = one_argument(argument, arg);
criteria->all = FALSE;
if ( criteria->class != -2 )
{
send_to_char("Duplicate entries for class.\n\r",
ch);
correct_entry = FALSE;
break;;
}
if ( ( criteria->class = class_lookup(arg) ) == -1 )
{
sprintf(buf, "Invalid class name: \"%s\"\n\r", arg);
send_to_char(buf,ch);
correct_entry = FALSE;
break;
}
continue;
}
else if ( !str_prefix( arg, "laston" ) || !str_prefix( arg,
"level" ) )
{
COMPARE_DATA *comparison_new;
/*
* We do allow multiple entries for laston and level,
* just in case someone wants to see everyone that has
* level > 30 and level < 50 or something... so we don't
* bother checking for duplicate entries first...
*/
criteria->all = FALSE;
comparison_new = new_compare();
comparison_new->name = str_dup( arg ); /* Store name of
comparison */
argument = one_argument(argument, arg); /* Get next
element, the operator */
if ( strlen(arg) > 1 ) /* Is operator more than 1
character? We only allow >, <, and = */
{
sprintf(buf,"Invalid operator: \"%s\"\n\r", arg);
send_to_char(buf,ch);
correct_entry = FALSE;
break;
}
comparison_new->operator = arg[0]; /* Store operator */
argument = one_argument(argument, arg); /* Get next
element, the compare value */
if ( !is_number(arg) ) /* We only allow numeric values
for comparisons */
{
sprintf(buf,"Invalid comparison value: \"%s\"\n\r",
arg);
send_to_char(buf,ch);
correct_entry = FALSE;
break;
}
comparison_new->value = atoi(arg);
comparison_new->next = criteria->comparison; /* Now tack
it onto the list of checks */
criteria->comparison = comparison_new;
continue;
}
else if ( !str_prefix( arg, "name" ) )
{
argument = one_argument(argument, arg);
criteria->all = FALSE;
if ( !IS_NULLSTR(criteria->name) )
{
send_to_char("Duplicate entries for name.\n\r", ch);
correct_entry = FALSE;
break;
}
criteria->name = str_dup(arg);
continue;
}
else if ( !str_prefix( arg, "race" ) )
{
argument = one_argument(argument, arg);
criteria->all = FALSE;
if ( criteria->race != -2 )
{
send_to_char("Duplicate entries for race.\n\r", ch);
correct_entry = FALSE;
break;
}
if ( ( criteria->race = race_lookup(arg) ) == -1 )
{
sprintf(buf, "Invalid race name: \"%s\"\n\r", arg);
send_to_char(buf,ch);
correct_entry = FALSE;
break;
}
continue;
}
else if ( !str_prefix( arg, "sort" ) )
{
argument = one_argument(argument, arg);
/*
* Any additional fields you want to add for sorting,
simply
* define a new value for them, add the string check
below,
* add the new value to the switch statement in the
sorting
* function above, and then create a new private
function for
* qsort to sort the list with...
*/
if ( sorted )
{
send_to_char("Duplicate sorting options.\n\r", ch);
correct_entry = FALSE;
break;
}
sorted = TRUE;
if ( !str_prefix( arg, "clan" ) )
criteria->sort = BYCLAN;
else if ( !str_prefix( arg, "class" ) )
criteria->sort = BYCLASS;
else if ( !str_prefix( arg, "laston" ) )
criteria->sort = BYLASTON;
else if ( !str_prefix( arg, "level" ) )
criteria->sort = BYLEVEL;
else if ( !str_prefix( arg, "name" ) )
criteria->sort = BYNAME;
else if ( !str_prefix( arg, "race" ) )
criteria->sort = BYRACE;
continue;
}
else
{
sprintf(buf, "Invalid search type: \"%s\"\n\r", arg);
send_to_char(buf,ch);
correct_entry = FALSE;
break;
}
}
if ( !correct_entry ) /* Destroy and free any memory that was
assigned */
{
COMPARE_DATA *comparison_new;
/*
* Any additional fields you may add to the compare data
structure,
* make sure you free any necessary variables below in order
to avoid
* memory leaks...
*/
if ( !IS_NULLSTR(criteria->name) )
free_string(criteria->name);
if ( criteria->comparison )
{
for ( ; criteria->comparison != NULL;
criteria->comparison = comparison_new )
{
comparison_new = criteria->comparison->next;
free_compare(criteria->comparison);
}
}
return FALSE;
}
}
return TRUE;
}
void do_plist( CHAR_DATA *ch, char *argument )
{
SEARCH_DATA *criteria;
int count = 0, x;
/* First things first, let's initialize criteria */
criteria = malloc( sizeof( SEARCH_DATA ) );
criteria->name = str_dup( "" );
criteria->race = -2;
criteria->clan = -2;
criteria->sort = BYNAME;
criteria->all = FALSE;
criteria->comparison = NULL;
/* Now to parse out the command line into the actual criteria... */
if ( !get_plist_criteria( ch, argument, criteria ) ) /* If FALSE,
something went wrong */
return;
/*
* Ok, if we've reached this point, it means that we've entered
* a line of queries with the appropriate syntax and we're ready
* to find all the matching pfiles now... so here we go...
*/
if ( ( count = get_plist_matches( criteria ) ) > 0 )
{
/*
* Found at least 1 matching pfile, now sort them and display
* them to the user...
*/
sort_plist_matches( count, criteria->sort );
display_plist_output( ch, count );
}
else
{
send_to_char("No matching players were found.\n\r", ch);
return;
}
/*
* Now free all the name strings that have been assigned as the
* plist was built, and then free the whole chunk of memory for
* the array itself along w/ the chunk for criteria...
*/
for ( x = 0; x < count - 1; x++ )
if ( !IS_NULLSTR(pfile_matches[x].name) )
free_string(pfile_matches[x].name);
free(criteria);
free(pfile_matches);
}