Dette er en flerdelt meddelelse i MIME-format.
--------------4BFD415BAB4F4E0AA2A79AEB
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Hi, I'm on the Mud-dev list as you've probably guessed. Here is the
piece of code Richard posted on Usenet (I found it on DejaNews so you
wouldn't have to).
--
,-----------------------------------------------------------.
| Per Vognsen (Phaerus) Programmer at Ashandar LLC |
| vognsen@post10.tele.dk ICQ: 12808824 |
`-----------------------------------------------------------'
--------------4BFD415BAB4F4E0AA2A79AEB
Content-Type: text/plain; charset=us-ascii; name="namecode.xp"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="namecode.xp"
From: Richard Woolcock <KaVir@nospam.dial.pipex.com>
Subject: [DESIGN] Naming policy code
Date: 25 Jun 1998 00:00:00 GMT
Message-ID: <359324DA.22BE@nospam.dial.pipex.com>
Content-Type: multipart/mixed; boundary="------------2C0E75DCB17"
Organization: None
Mime-Version: 1.0
Newsgroups: alt.mud.programming
This is a multi-part message in MIME format.
--------------2C0E75DCB17
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
A couple of weeks ago, there was a discussion regarding naming policies,
and the implementation of a coded system to deal with this problem. The
implication was made that such a feat would take considerable skill, whilst
I argued that this was not the case. In the last two week I have spent
about half my lunch break every working day developing such a system, which
I finished tidying up last night. The total time spent working on the code
was approximately ten hours, with about four hours spent on the main functi-
onality, two hours on the race naming, and another four hours tidying up and
testing the code. This was longer than I expected, but is possibly due to my
inexperience in this particular field of coding.
The two files included at the bottom of this post are 'name.h' and 'name.c'
respectively, and the code is designed to be codebase independant - in fact,
it can be run without any mud at all (simply compile the name.c file and run
it as is).
The solution is not the best possible, nor can it completely replace human
judgement of names. It will, however, allow the implementors of a mud using
it to ensure that every name chosen is pronouncable, and optionally, that it
follows a number of race-specific rules (such as containing or not containing
certain letters, combinations of letters, numbers of syllables, etc).
The main purpose of this posting is to prove that such a system is easily
implemented, and to back up my claim with proof. If you wish to use this
code in your mud you may do so.
KaVir.
--------------2C0E75DCB17
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="NAME.H"
/******************************************************************************
Snippet: Automatic naming policy code (v1.0).
Author: Richard Woolcock (aka KaVir).
Date: 25th June 1998.
******************************************************************************
This code is copyright (C) 1998 by Richard Woolcock. It may be used and
distributed freely, as long as you don't remove this copyright notice.
******************************************************************************/
/******************************************************************************
Remove the comments from the statement below before inserting this snippet
into your mud.
******************************************************************************/
/* #define MUD_VERSION */
/******************************************************************************
Required enumerated types
******************************************************************************/
typedef enum /**************************************/
{ /* Enumerated type used to indicate */
RACE_HUMAN, /* if a letter is a vowel or a non- */
RACE_ELF, /* vowel. */
RACE_DWARF, /**************************************/
RACE_MAX /* This must always be the last RACE_TYPE value. */
} RACE_TYPE;
/******************************************************************************
Required global operation prototypes
******************************************************************************/
char * Name_invalid ( char *argument );
/* Operation to check if the argument is a valid name. */
char * Name_unacceptable ( char *argument, RACE_TYPE race );
/* Operation to check if name is valid for the race. */
--------------2C0E75DCB17
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="NAME.C"
/******************************************************************************
Snippet: Automatic naming policy code (v1.0).
Author: Richard Woolcock (aka KaVir).
Date: 25th June 1998.
******************************************************************************
This code is copyright (C) 1998 by Richard Woolcock. It may be used and
distributed freely, as long as you don't remove this copyright notice.
******************************************************************************/
/******************************************************************************
Required libraries
******************************************************************************/
#include <stdio.h>
#include "name.h"
#ifdef MUD_VERSION
#include <string.h>
#endif
/******************************************************************************
Required literals
******************************************************************************/
#define MIN_NAME 3 /* Minimum length of a character name. */
#define MAX_NAME 14 /* Maximum length of a character name. */
/******************************************************************************
Required enumerated types
******************************************************************************/
typedef enum /**************************************/
{ /* Standard boolean type, containing */
FALSE, /* the values TRUE and FALSE. */
TRUE /**************************************/
} BOOLEAN_TYPE;
typedef enum /**************************************/
{ /* Enumerated type used to determine */
START, /* what type of letter (vowel or non- */
MIDDLE, /* vowel) the previous letter was */
FINISH /* preceded by. */
} POSITION_TYPE; /**************************************/
typedef enum /**************************************/
{ /* Enumerated type used to indicate */
VOWEL, /* if a letter is a vowel or a non- */
NORMAL, /* vowel. */
END /**************************************/
} LOCATION_TYPE;
typedef enum /**************************************/
{ /* Enumerated type used to indicate */
MODE_PREFIX, /* the currently used mode of string */
MODE_INFIX, /* searching. */
MODE_POSTFIX /**************************************/
} MODE_TYPE;
/******************************************************************************
Required structure types
******************************************************************************/
typedef struct /**************************************/
{ /* Structure used to contain both an */
char *letters; /* alphabetic character, and its type */
LOCATION_TYPE location; /* (vowel or non-vowel). */
} LETTER_TYPE; /**************************************/
typedef struct /**************************************/
{ /* Structure used to contain the data */
char * name; /* about required and banned letter */
short syl_min; /* combinations for different races, */
short syl_max; /* as well as min and max number of */
char * required; /* syllables, and the name itself. */
char * banned; /**************************************/
} RACE_DATA_TYPE;
/******************************************************************************
Required macros
******************************************************************************/
#define LOWER(c) ( ( ('A' <= (c)) && ((c) <= 'Z') ) ? ((c)+32) : (c) )
/* Returns the lower case value of the letter 'c'. */
#define IS_LOWER(c) ( ( ('a' <= (c)) && ((c) <= 'z') ) ? 1 : 0 )
/* Returns TRUE if the character 'c' is a lower case letter. */
#define IS_UPPER(c) ( ( ('A' <= (c)) && ((c) <= 'Z') ) ? 1 : 0 )
/* Returns TRUE if the character 'c' is an upper case letter. */
#define IS_LETTER(c) ( IS_LOWER(c) | IS_UPPER(c) )
/* Returns TRUE if the character 'c' is a letter. */
#define VALUE(c) ( (c) <= 'Z' ? ((c) - 'A') : ((c) - 'a') )
/* Returns the look-up table value of the letter 'c'. */
/******************************************************************************
Required local operation prototypes
******************************************************************************/
static BOOLEAN_TYPE name_contains ( char *part, char *whole );
/* Operation to find part strings within whole string. */
static void check_vowel ( char *argument );
/* Operation to check the validity of a vowel. */
static void check_non_vowel ( char *argument );
/* Operation to check the validity of a non-vowel. */
static BOOLEAN_TYPE letter_combo ( char a, char b );
/* Operation to check if two letters can be combined. */
static BOOLEAN_TYPE wstr_prefix ( char *part, char *whole );
/* Operation to find string prefix (with wildcard). */
static BOOLEAN_TYPE wstr_infix ( char *part, char *whole );
/* Operation to find string infix (with wildcard). */
static BOOLEAN_TYPE wstr_postfix ( char *part, char *whole );
/* Operation to find string postfix (with wildcard). */
/******************************************************************************
Required local variables
******************************************************************************/
static char invalid_store [80] = {'\0'};
/* Array to contain return message */
static int vcount = 0; /* Vowel counter. */
static int ncount = 0; /* Non-vowel counter. */
static int scount = 0; /* Syllibal counter. */
static POSITION_TYPE prev_vowel = START; /* Position of the previous vowel. */
static char * BANNED_LIST = "|silly dood "; /* Banned words. */
/******************************************************************************
Required local constants
******************************************************************************/
static const LETTER_TYPE letter[] {
{"a", VOWEL}, /**************************************/
{"b", NORMAL}, /* Look-up table used to determine */
{"c", NORMAL}, /* whether a letter is a vowel or a */
{"d", NORMAL}, /* non-vowel. */
{"e", VOWEL}, /**************************************/
{"f", NORMAL},
{"g", NORMAL},
{"h", NORMAL},
{"i", VOWEL},
{"j", NORMAL},
{"k", NORMAL},
{"l", NORMAL},
{"m", NORMAL},
{"n", NORMAL},
{"o", VOWEL},
{"p", NORMAL},
{"q", NORMAL},
{"r", NORMAL},
{"s", NORMAL},
{"t", NORMAL},
{"u", VOWEL},
{"v", NORMAL},
{"w", NORMAL},
{"x", NORMAL}, /**************************************/
{"y", VOWEL}, /* The letter 'y' is a special case. */
{"z", NORMAL} /**************************************/
};
static const RACE_DATA_TYPE RACE_TABLE[RACE_MAX] {
/************************* **************************************/
/* Note the following: * * In order to qualify for that race, */
/* > := Prefix mode. * * the player must have at least one */
/* | := Infix mode. * * letter-combination, and have none */
/* < := Postfix mode. * * of the banned letter-combinations. */
/************************* **************************************/
{ /* RACE_HUMAN */
"human", /* Race name. */
1, 3, /* Min/Max syllables in name. */
">? ", /* Required. */
">z x |ug >og " /* Banned. */
},
{ /* RACE_ELF */
"elf", /* Race name. */
3, 4, /* Min/Max syllables in name. */
">s?l |l?th l?sh <l le ", /* Required. */
">z x " /* Banned. */
},
{ /* RACE_DWARF */
"Dwarf", /* Race name. */
1, 2, /* Min/Max syllables in name. */
">n?r k?r t?r |ug og <rr ", /* Required. */
"|ll x gar " /* Banned. */
}
};
/******************************************************************************
Operation for independant testing
******************************************************************************/
#ifndef MUD_VERSION
void main()
{
char argument[80];
argument[0] = '\0';
/* Loop until the termination character '9' is entered */
while ( *argument != '9' )
{
/* Not interested in return values, as printf is called. */
(void) Name_invalid( argument );
(void) Name_unacceptable( argument, RACE_HUMAN );
printf( "\n\r\n\r" );
}
return;
}
#endif
/******************************************************************************
Global operations
******************************************************************************/
/* Operation to check if a name is valid or not. */
char *Name_invalid( char *argument )
{
/* Assign a temporary pointer */
char *ptr = argument;
/* Initialise the invalid_store */
invalid_store[0] = '\0';
/* Initialise the counter variables */
vcount = ncount = scount = 0;
#ifndef MUD_VERSION
/* Accept a single word name from the user */
printf("Enter a name: ");
scanf( "%s", argument );
printf("\n\rYou entered %s.\n\r",argument);
#endif
/* Loop through the string. */
while ( *ptr )
{
/* Ensure that the entire string is made up of only letters. */
if ( !IS_LETTER(*ptr) )
{
#ifndef MUD_VERSION
printf("You may only use letters.\n\r");
#else
sprintf( invalid_store, "You may only use letters.\n\r");
#endif
return ( &invalid_store[0] );
}
ptr++;
}
/* Ensure that the string is of a valid size. */
if ( strlen( argument ) < MIN_NAME || strlen( argument ) > MAX_NAME )
{
#ifndef MUD_VERSION
printf( "Name must be %d-%d letters long.\n\r", MIN_NAME, MAX_NAME );
#else
sprintf( invalid_store, "Name must be %d-%d letters long.\n\r",
MIN_NAME, MAX_NAME );
#endif
return ( &invalid_store[0] );
}
if ( letter[VALUE(*argument)].location == VOWEL )
{
/* The next letter is a vowel. */
prev_vowel = FINISH;
check_vowel( argument );
}
else
{
/* The next letter is a non-vowel. */
prev_vowel = START;
check_non_vowel( argument );
}
/* Ensure the name doesn't end half way through a syllable. */
if ( prev_vowel == FINISH )
{
#ifndef MUD_VERSION
printf( "Illegal name ending.\n\r" );
#else
sprintf( invalid_store, "Illegal name ending.\n\r" );
#endif
return ( &invalid_store[0] );
}
/* Check for valid name parts. */
if ( name_contains( BANNED_LIST, argument ) )
{
#ifndef MUD_VERSION
printf( "Illegal name syntax, try again.\n\r" );
#else
sprintf( invalid_store, "Illegal name syntax, try again.\n\r" );
#endif
return ( &invalid_store[0] );
}
#ifndef MUD_VERSION
printf( "Vowels: %d, Non-vowels: %d, Syllables: %d.\n\r",
vcount, ncount, scount );
#endif
/* The name is not invalid */
return ( NULL );
}
/* Operation to check if a name is valid for that race. */
char *Name_unacceptable( char *argument, RACE_TYPE race )
{
/* Check for valid name parts. */
if ( name_contains( BANNED_LIST, argument ) )
{
#ifndef MUD_VERSION
printf( "Illegal name syntax, try again.\n\r" );
#else
sprintf( invalid_store, "Illegal name syntax, try again.\n\r" );
#endif
return ( &invalid_store[0] );
}
if ( race < RACE_MAX )
{
/* Check for valid name parts. */
if ( !name_contains( RACE_TABLE[race].required, argument )
|| name_contains( RACE_TABLE[race].banned, argument )
|| scount < RACE_TABLE[race].syl_min
|| scount > RACE_TABLE[race].syl_max )
{
#ifndef MUD_VERSION
printf( "That is not a fitting %s name.\n\r",
RACE_TABLE[race].name );
#else
sprintf( invalid_store, "That is not a fitting %s name.\n\r",
RACE_TABLE[race].name );
#endif
return ( &invalid_store[0] );
}
}
/* The name is not unacceptable */
return ( NULL );
}
/******************************************************************************
Local operations
******************************************************************************/
/* Operation returns TRUE if any 'parts' are within 'whole'. */
static BOOLEAN_TYPE name_contains( char *part, char *whole )
{
char buf [80];
int count = 0;
MODE_TYPE mode = MODE_PREFIX;
do /* Loop through the 'part' string. */
{
/* Check the current 'part' character. */
switch ( LOWER(*part) )
{
case '>': /* Prefix character. */
mode = MODE_PREFIX;
count = 0;
break;
case '|': /* Infix character. */
mode = MODE_INFIX;
count = 0;
break;
case '<': /* Postfix character. */
mode = MODE_POSTFIX;
count = 0;
break;
case 'a': /* Alphabetic characters. */
case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k':
case 'l': case 'm': case 'n': case 'o': case 'p':
case 'q': case 'r': case 's': case 't': case 'u':
case 'v': case 'w': case 'x': case 'y': case 'z':
case '?': /* Wildcard character. */
buf[count] = *part;
count++;
break;
default: /* Other characters. */
buf[count] = '\0';
switch ( mode )
{
case MODE_PREFIX:
/* Check for 'part' at the front of 'whole'. */
if ( wstr_prefix( buf, whole ) )
return ( TRUE );
break;
case MODE_INFIX:
/* Check for 'part' anywhere within 'whole'. */
if ( wstr_infix( buf, whole ) )
return ( TRUE );
break;
case MODE_POSTFIX:
/* Check for 'part' at the end of 'whole'. */
if ( wstr_postfix( buf, whole ) )
return ( TRUE );
break;
}
count = 0;
break;
}
}
while ( *++part );
return ( FALSE );
}
/* Checks if the letter is a valid vowel. */
static void check_vowel( char *argument )
{
/* Check that the end of the string has not been reached. */
if ( *argument == '\0' )
{
return;
}
/* Increment the number of vowels and syllables. */
++vcount;
++scount;
/* Check what the previous letter was. */
switch ( prev_vowel )
{
case START: /* The first part of a non-vowel. */
prev_vowel = MIDDLE; /* This will be a vowel. */
break;
case MIDDLE: /* Another vowel. */
#ifndef MUD_VERSION
printf( "Illegal name: Misplaced letter '%c'.\n\r", *argument );
#else
sprintf( invalid_store, "Illegal name: Misplaced letter '%c'.\n\r",
*argument );
#endif
return;
case FINISH: /* The second part of a non-vowel. */
prev_vowel = MIDDLE; /* This will be a vowel. */
break;
}
/* Y isn't really a vowel, so check if this 'vowel' is the letter 'y'. */
if ( *argument == 'y' )
{
/* Decrement number of vowels and increment number of non-vowels. */
--vcount;
++ncount;
}
/* Only do this if there is another character in the string. */
if ( *(argument+1) )
{
/* Check if this and next letter are compatible vowels. */
if ( letter_combo( *argument, *(argument+1) ) &&
letter[VALUE(*(argument+1))].location == VOWEL )
{
/* Increment the number of vowels, ignore this letter. */
++vcount;
++argument;
}
}
/* Drop out if reached the end of the string. */
if ( *(argument+1) == '\0' )
{
/* The letter 'e' on the end of a name does not make a new syllable. */
if ( *argument == 'e' )
{
scount--;
}
return;
}
if ( letter[VALUE(*(argument+1))].location == VOWEL )
{
/* The next letter is a vowel. */
check_vowel( argument+1 );
}
else
{
/* The next letter is a non-vowel. */
check_non_vowel( argument+1 );
}
return;
}
/* Checks if the letter is a valid non-vowel. */
static void check_non_vowel( char *argument )
{
/* Check that the end of the string has not been reached. */
if ( *argument == '\0' )
{
return;
}
/* Increment the number of non-vowels. */
++ncount;
/* Check what the previous letter was. */
switch ( prev_vowel )
{
case START: /* The first part of a non-vowel. */
prev_vowel = FINISH; /* This will be the second part */
break;
case MIDDLE: /* A vowel. */
prev_vowel = START; /* This will be the first part of a non-vowel. */
break;
case FINISH: /* The second part of a non-vowel. */
#ifndef MUD_VERSION
printf( "Illegal name: Misplaced letter '%c'.\n\r", *argument );
#else
sprintf( invalid_store, "Illegal name: Misplaced letter '%c'.\n\r",
*argument );
#endif
return;
}
/* Check for two valid non-vowels in a row */
if ( *(argument+1) )
{
if ( letter_combo( *argument, *(argument+1) ) &&
letter[VALUE(*(argument+1))].location != VOWEL )
{
/* Increment the number of non-vowels, ignore this letter. */
++ncount;
++argument;
}
}
/* Drop out if reached the end of the string */
if ( *(argument+1) == '\0' )
{
return;
}
if ( letter[VALUE(*(argument+1))].location == VOWEL )
{
/* The next letter is a vowel. */
check_vowel( argument+1 );
}
else
{
/* The next letter is a non-vowel. */
check_non_vowel( argument+1 );
}
return;
}
/* Checks the two input letters to see if they fit together */
static BOOLEAN_TYPE letter_combo ( char a, char b )
{
BOOLEAN_TYPE valid = FALSE;
switch ( LOWER(a) )
{
default: break;
case 'a': /* Vowels use inversed method */
switch ( LOWER(b) )
{
case 'a':
break;
default:
valid = TRUE;
break;
}
break;
case 'b':
switch ( LOWER(b) )
{
case 'l':
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'c':
switch ( LOWER(b) )
{
case 'h':
case 'k':
case 'l':
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'd':
switch ( LOWER(b) )
{
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'e': /* Vowels use inversed method */
valid = TRUE;
break;
case 'f':
switch ( LOWER(b) )
{
case 'l':
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'g':
switch ( LOWER(b) )
{
case 'h':
case 'l':
case 'n':
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'h':
switch ( LOWER(b) )
{
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'i': /* Vowels use inversed method */
switch ( LOWER(b) )
{
case 'i':
break;
default:
valid = TRUE;
break;
}
break;
case 'j':
switch ( LOWER(b) )
{
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'k':
switch ( LOWER(b) )
{
case 'h':
case 'l':
case 'n':
case 'r':
case 'w':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'l':
switch ( LOWER(b) )
{
case 'd':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'm':
switch ( LOWER(b) )
{
case 'n':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'n':
switch ( LOWER(b) )
{
case 'd':
case 'g':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'o': /* Vowels use inversed method */
valid = TRUE;
break;
case 'p':
switch ( LOWER(b) )
{
case 'h':
case 'l':
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'q':
switch ( LOWER(b) )
{
case 'u':
valid = TRUE;
break;
default:
break;
}
break;
case 'r':
switch ( LOWER(b) )
{
case 'd':
case 'l':
case 'k':
/* Only valid at the end of a syllable */
if ( prev_vowel != FINISH )
valid = TRUE;
break;
case 'h':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 's':
switch ( LOWER(b) )
{
case 'c':
case 'h':
case 'k':
case 'l':
case 'm':
case 'n':
case 'p':
case 'r':
case 't':
case 'w':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 't':
switch ( LOWER(b) )
{
case 'h':
case 'r':
case 'w':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'u': /* Vowels use inversed method */
switch ( LOWER(b) )
{
case 'u':
break;
default:
valid = TRUE;
break;
}
break;
case 'v':
switch ( LOWER(b) )
{
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'w':
switch ( LOWER(b) )
{
case 'h':
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
case 'x':
switch ( LOWER(b) )
{
case 't':
valid = TRUE;
break;
default:
break;
}
break;
case 'y': /* Vowels use inversed method */
switch ( LOWER(b) )
{
case 'y':
break;
default:
valid = TRUE;
break;
}
break;
case 'z':
switch ( LOWER(b) )
{
case 'r':
case 'y':
valid = TRUE;
break;
default:
break;
}
break;
}
return ( valid );
}
/* str_prefix with '?' wildcards, non-case sensitive */
static BOOLEAN_TYPE wstr_prefix( char *part, char *whole )
{
do /* Loop through the characters in the 'whole' and 'part' strings */
{
/* Check if part and whole match up, with '?' as a wildcard */
if ( LOWER(*part) != LOWER(*whole) && *part != '?' )
{
return ( FALSE );
}
}
while ( *++part && *++whole ); /* Until one string is empty */
if ( *part ) /* If the 'part string' is empty, the input is bad */
{
return ( FALSE );
}
return ( TRUE );
}
/* str_infix with '?' wildcards, non-case sensitive */
static BOOLEAN_TYPE wstr_infix( char *part, char *whole )
{
do /* Loop through the characters in the 'whole' string */
{
/* Perform a prefix check at this position in the 'whole' string */
if ( wstr_prefix( part, whole ) )
{
return ( TRUE );
}
}
while ( *whole++ ); /* Until the whole string runs of of characters */
return ( FALSE );
}
/* str_postfix with '?' wildcards, non-case sensitive */
static BOOLEAN_TYPE wstr_postfix( char *part, char *whole )
{
int whole_len = strlen( whole );
int part_len = strlen( part );
/* Make sure the string isn't too short to check for */
if ( whole_len < part_len )
{
return ( FALSE );
}
/* Position the whole pointer to check the last <part> characters */
whole += ( whole_len - part_len );
do /* Loop through the characters in the 'whole' and 'part' strings */
{
/* Check if part and whole match up, with '?' as a wildcard */
if ( LOWER(*part) != LOWER(*whole) && *part != '?' )
{
return ( FALSE );
}
}
while ( *++part && *++whole ); /* Until one string is empty */
if ( *part ) /* If the 'part string' is empty, the input is bad */
{
return ( FALSE );
}
return ( TRUE );
}
--------------2C0E75DCB17--
--------------4BFD415BAB4F4E0AA2A79AEB--