Coding Sample

This is a program I wrote a while back and I present it here as an example of my coding style.
A couple of things I strive for in code is neatness and lots of comments.
And I try not to be obtuse. I know it's kind of fun to be terse and efficient, even clever,
but in the end this stuff has to be maintained and keeping it simple/stupid
is probably a better strategy. I always spent time designing a good log file for
troubleshooting down the road.

What follows is a COM object written in C, to be used by ASP/IIS websites to create a unique identifier
for a person and add a record to a LDAP database, making sure to avoid duplicates.
The new id is accessed by the PersonNumber property of the object and any
error messages are accessed in the Messages property, which is a 2-dimensional
array. This was my first COM object to be consumed by VB code in an ASP page and it took
a week to get the 2-dimensional array passed back to the web page. I thought I had the
problem licked when I first tried passing just an int, but each move to the next level of
complexity, a string, a one dimensional array and finally the 2-d array, was a new challenge.
I had no idea what I was doing, but that's the fun part.

The consuming code in the ASP page would look like this:

Set objPN = Server.CreateObject("AssignPNObject.AssignPNObject.1")

retCode = objPN.AssignNumber(ssn, firstName, middleInitial , lastName,
            birthDate, gender, forceNumberIndicator)
num = objPN.PersonNumber
if retCode = 0 then
    response.write("Person Number : " & num)
elseif retCode = 4 then
    response.write("Person Number : " & num)
elseif retCode = 6 then
    response.write("An Existing Person Number was returned : " & num)
elseif retCode = 8 then
    response.write("Validations error. No person Number assigned.")
elseif retCode = 12 then
    response.write("Failed Assign attempt. ")
end if
if retCode > 0 then
    msgQuan = objPN.msgQuantity
   if msgQuan > 0 then
       response.write("Num messages : " & msgQuan)
       msgs = objPN.Messages
       response.write("ubound dim 1 : " & UBound(msgs,1))
       response.write("ubound dim 2 : " & UBound(msgs, 2)

       for i = lbound(msgs,1) to ubound(msgs,1)
          response.write("Num : " & msgs(i,0) )
          response.write("Sev : " & msgs(i,1))
          response.write("Text : " & msgs(i,2))
       next
    end if
end if

set objPN = Nothing

And here's the C code for the COM object.

// AssignPNObject.cpp : Implementation of CAssignPNObject

#include "stdafx.h"
#include "AssignPersonNumber.h"
#include "AssignPNObject.h"

/////////////////////////////////////////////////////////////////////////////
// CAssignPNObject

STDMETHODIMP CAssignPNObject::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] =
	{
		&IID_IAssignPNObject,
	};
	for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

STDMETHODIMP CAssignPNObject::AssignNumber(BSTR bstrInSSN,
								BSTR bstrInFN,
								BSTR bstrInMI,
								BSTR bstrInLN,
								BSTR bstrInBD,
								BSTR bstrInG,
								BSTR bstrInFNI,
								short *pVal)
{

	m_msgQuantity = 0;			// initial number of msgs to zero
	m_PersonNumber = 0;

   	// The input parms come in as BSTR's, but the LDAP API wants to see strings,
	// so the BSTR's must be converted to strings.  This is a 2 step process -
	// 1. first convert BSTR to multiBytes and then to strings (with a strcpy).

	// For each input parameter:
	// 1. declare the 2 element array the first elem will hold the string attr value
	// 2. declare a string and a multibye to hold the data during the conversion
	// 3. convert from bstr to multibyte using the windows API WideCharToMultiByte
	// 4. convert the multibyte to string using strcpy

	// SSN
	char strSSN[10] = "";
	TCHAR mbSSN[10] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInSSN, -1, mbSSN,10, NULL, NULL);
    strcpy(strSSN, mbSSN);

	// first name - given name
	char strFN[31] = "";
	TCHAR mbFN[31] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInFN, -1, mbFN,31, NULL, NULL);
    strcpy(strFN, mbFN);

	// middle initial
	//char strMI[2];
	char strMI[2] = "";
	TCHAR mbMI[2] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInMI, -1, mbMI,2, NULL, NULL);
    strcpy(strMI, mbMI);

	// sn - sur name - last name
    char strLN[31] = "";
	TCHAR mbLN[31] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInLN, -1, mbLN, 31 , NULL, NULL);
    strcpy(strLN, mbLN);

	// birth date
	char strBD[11] = "";
	TCHAR mbBD[11] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInBD, -1, mbBD,11, NULL, NULL);
    strcpy(strBD, mbBD);

	// gender
	char strG[2] = "";
	TCHAR mbG[2] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInG, -1, mbG, 2 , NULL, NULL);
    strcpy(strG, mbG);

	// force number indicator
	char strFNI[2] = "";
	TCHAR mbFNI[2] = "";
	WideCharToMultiByte(CP_ACP, 0, bstrInFNI, -1, mbFNI,2, NULL, NULL);
    strcpy(strFNI, mbFNI);

	// Now that we have strings for all the input params,
	// let's do the edits on the input data
	int validData = 0;
	validData = validateInput( strSSN,  strFN, strMI, strLN, strBD, strG, strFNI);
	if (validData > 3)
	{
		*pVal = 8;
		return S_OK;
	}
	//***********************************************************************
	// OK time to assign the person number.
	// Note that more checking goes on in assignNumber - looking for an entry
	// with attributes that match or come close to matching the current input
	// params.  If a match is found, a new person number entry is not done,
	// so at this point, all may not be hunky dory.
	//3000-AssignNumber Procedure
	int retCode = 0;
	retCode = assignTheNumber( strSSN, strFN, strMI, strLN, strBD, strG, strFNI);

	*pVal = retCode;

	return S_OK;
}

//*****************************************************************************
//****************            END OF MAIN              ************************
//*****************************************************************************
//*****************************************************************************


//*****************************************************************************
//*****    All the get property functions exposed to the client are here ******
//*****************************************************************************

STDMETHODIMP CAssignPNObject::get_MsgQuantity(short * pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_msgQuantity;
	return S_OK;
}

STDMETHODIMP CAssignPNObject::get_PersonNumber(long * pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_PersonNumber;
	return S_OK;
}

STDMETHODIMP CAssignPNObject::get_Messages(VARIANT * pVal)
{
	int lgth = 0;
	char strMsgNum[200] = "";
	char strMsgSev[2] = "";
	char strMsgText[144] = "";
	char strBB[200] = "";
	int z = 0;
	int row = 0;
	int i = 0;
	long ndx[2];
	CComBSTR bstrVal(200, strBB);
	CComVariant varVal;

	SAFEARRAYBOUND bounds[] = {{m_msgQuantity,0}, {3, 0}};
	VariantInit(pVal);
	pVal->vt = VT_VARIANT | VT_ARRAY;
	pVal->parray = SafeArrayCreate(VT_VARIANT, 2, bounds);

	TCHAR mbMsg[200];
	std::vector<CComBSTR>::iterator it;
	char strMessage[200];
	for (i = 0; i < 200; i++ )
		strMessage[i] = NULL;


	for (it = m_vecMsgs.begin(); it != m_vecMsgs.end(); it++, row++)
	{
		WideCharToMultiByte(CP_ACP, 0, ((*it).m_str), -1, mbMsg,200, NULL, NULL);
		strcpy(strMessage, mbMsg);

		strncpy(strMsgNum, strMessage, 6);
		strMsgNum[6] = '\0';

		bstrVal.Empty();
		bstrVal.Append(strMsgNum);
		varVal = bstrVal;
		ndx[0] = row;
		ndx[1] = 0;
		SafeArrayPutElement(pVal->parray, ndx, &varVal);

		// Now do the severity flag
		strMsgSev[0] = strMessage[7];
		strMsgSev[1] = '\0';
		bstrVal.Empty();
		bstrVal.Append(strMsgSev);
		varVal = bstrVal;
		ndx[0] = row;
		ndx[1] = 1;
		SafeArrayPutElement(pVal->parray, ndx, &varVal);

		// Now do the text
		lgth = strlen(strMessage);
		//lgth = lgth - 8;
		for (i = 9; i < lgth; i++)
		{
				strMsgText[z] = strMessage[i];
				z++;
		}
		strMsgText[z] = '\0';
		bstrVal.Empty();
		bstrVal.Append(strMsgText);
		varVal = bstrVal;
		ndx[0] = row;
		ndx[1] = 2;
		SafeArrayPutElement(pVal->parray, ndx, &varVal);
		for (i = 0; i < lgth; i++ )
			strMsgText[i] = NULL;
		z = 0;


	}

	return S_OK;
}




//************************************************************************
//************************************************************************
//******************  Input fields validation routines  ******************
//************************************************************************
//************************************************************************
//2000-validateInput
int CAssignPNObject::validateInput(char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
	int goodDataFlag = 0;
	int retVal = 0;
	if (strlen(strSSN) > 0)
	{
		goodDataFlag = validateSSN(  strSSN );
		if ( goodDataFlag != 0)
		{
			retVal = goodDataFlag;
			goodDataFlag = 0;
		}
	}
	goodDataFlag = validateGenderCode(strG);
	if ( goodDataFlag != 0)
	{
		retVal = goodDataFlag;
		goodDataFlag = 0;
	}
	goodDataFlag = validateName(  strFN,  strMI, strLN );
	if ( goodDataFlag != 0)
	{
		retVal = goodDataFlag;
		goodDataFlag = 0;
	}
	goodDataFlag = validateBirthDate( strBD );
	if ( goodDataFlag != 0)
	{
		retVal = goodDataFlag;
		goodDataFlag = 0;
	}
	goodDataFlag = validateGenderCode(  strG );
	if ( goodDataFlag != 0)
	{
		retVal = goodDataFlag;
		goodDataFlag = 0;
	}
	goodDataFlag = validateFNI( strFNI, strSSN);
	if ( goodDataFlag != 0)
	{
		retVal = goodDataFlag;
		goodDataFlag = 0;
	}


	return retVal;
}
//2100-validateSSN
int CAssignPNObject::validateSSN( char* strSSN )
{
	int retVal = 0;
	int badFlag = 0;

	if (strlen(strSSN) < 9 || strlen(strSSN) > 9)
		badFlag = 1;

	if (badFlag == 0)
	{
		for (int i = 0; i < 9; i++)
		{
			if (isdigit( strSSN[i] ) == 0)
			{
				badFlag = 1;
			}
		}
	}
	// Now make sure ssn is in range
	if (badFlag == 0)
	{
		int ssn = atoi( strSSN );
		if ( (ssn < 1000000 && ssn != 0) || (ssn > 766999999) )
		{
			badFlag = 1;
		}
	}
	if (badFlag == 1)
	{
		char err[140] = "PN0010!R!invalid SSN ";
		strcat( err, strSSN );
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}


	return retVal;
}
//2200-validateName
int CAssignPNObject::validateName( char* strFN, char* strMI, char* strLN )
{
	int retVal = 0;
	int badFlag = 0;
	int i = 0;
	if (strcmp(strFN, "") == 0 )
	{
		badFlag = 1;
		char err[140] = "PN0002!R!first name required";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}

	if (strcmp(strLN, "") == 0 )
	{
		char err[140] = "PN0005!R!last name required";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}

	if (strlen(strFN) > 30)
	{
		char err[140] = "PN0004!R!first name length > 30 characters";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}

	if (strlen(strLN) > 30)
	{
		badFlag = 1;
		char err[140] = "PN0004!R!last name length > 30 characters";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}

	if (strcmp( strMI, " ") == 0)
		strcpy (strMI, " ");

	if (strlen(strMI) > 1)
	{
		char err[140] = "PN0004!R!middle initial length > 1 character";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}


	for ( i = 0; i < strlen(strFN); i++ )
	{
		if ( !(isalpha( strFN[i] )) )
		{
			if (  strFN[i]  != ' ' && strFN[i]  != '.' && strFN[i]  != 39 && strFN[i]  != '-')
			{
				char err[140] = "PN0004!R!first name : ";
				strcat( err, strFN);
				strcat(err, " not alphabetic");
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				retVal = 8;
			}

		}
	}
	for ( i = 0; i < strlen(strLN); i++ )
	{
		if ( !(isalpha( strLN[i] )) )
		{
			if (  strLN[i]  != ' ' && strLN[i]  != '.' && strLN[i]  != 39 && strLN[i]  != '-')
			{
				char err[140] = "PN0004!R!last name : ";
				strcat( err, strLN);
				strcat(err, " not alphabetic");
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				retVal = 8;
			}
		}
	}
	for ( i = 0; i < strlen(strMI); i++ )
	{
		if ( !(isalpha( strMI[i] )) )
		{
			if (  strMI[i]  != ' ')
			{
				char err[140] = "PN0004!R!middle initial : ";
				strcat( err, strMI);
				strcat(err, " not alphabetic");
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				retVal = 8;
			}

		}
	}


	return retVal;
}


//2300-validateBirthDate
int CAssignPNObject::validateBirthDate( char* strBD )
{
	int retVal = 0;
	int badFlag = 0;
	// get the rundate year
	int todaysYear;
	time_t timer;
	struct tm *sys_time;
	time(&timer);
	sys_time = localtime(&timer);
	todaysYear = sys_time->tm_year - 100 + 2000;


	if (strcmp(strBD, "") == 0 )
	{
		badFlag = 1;
		char err[140] = "PN0002!R!birth date required";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}
	else
	{
		if (isdigit( strBD[0] ) == 0)
			badFlag = 1;
		else if (isdigit( strBD[1] ) == 0)
			badFlag = 1;
		else if (strBD[2] != '/')
			badFlag = 1;
		else if (isdigit( strBD[3] ) == 0)
			badFlag = 1;
		else if (isdigit( strBD[4] ) == 0)
			badFlag = 1;
		else if (strBD[2] != '/')
			badFlag = 1;
		else if (isdigit( strBD[6] ) == 0)
			badFlag = 1;
		else if (isdigit( strBD[7] ) == 0)
			badFlag = 1;
		else if (isdigit( strBD[8] ) == 0)
			badFlag = 1;
		else if (isdigit( strBD[9] ) == 0)
			badFlag = 1;
		else
		{
			char dd[3] = "";
			char mm[3] = "";
			char yy[5] = "";

			mm[0] = strBD[0];
			mm[1] = strBD[1];
			mm[2] = '\0';
			dd[0] = strBD[3];
			dd[1] = strBD[4];
			dd[2] = '\0';
			yy[0] = strBD[6];
			yy[1] = strBD[7];
			yy[2] = strBD[8];
			yy[3] = strBD[9];
			yy[4] = '\0';

			int iyy = atoi( yy );
			int idd = atoi( dd );
			int imm = atoi( mm );
			if (iyy < 1900 || iyy > todaysYear)
				badFlag = 1;
			else
			if ( imm < 1 || imm > 12 )
				badFlag = 1;
			else
			if ( imm == 1 || imm == 3 || imm == 5 || imm == 7 || imm == 8 || imm == 10 || imm == 12)
			{
				if (idd < 1 || idd > 31 )
					badFlag = 1;
			}
			else
			if ( imm == 4 || imm == 6 || imm == 9 || imm == 11)
			{
				if (idd < 1 || idd > 30 )
					badFlag = 1;
			}
			else
			if (imm == 2)
			{
				if (( iyy % 4 ) == 0)
					if (idd < 1 || idd > 29 )
						badFlag = 1;
					else
						if (idd < 1 || idd > 28 )
						badFlag = 1;
			}
		}

		if (badFlag == 1)
		{
			char err[140] = "PN0002!R!invalid birth date : ";
			strcat( err, strBD);
			m_msgQuantity ++;
			m_vecMsgs.push_back(CComBSTR(err));
			retVal = 8;
		}
	}
	return retVal;
}
//2400-validateGenderCode
int CAssignPNObject::validateGenderCode( char* strG )
{
	int retVal = 0;
	if (strcmp(strG, "") == 0 )
	{
		char err[140] = "PN0013!R!gender required";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}
	else
	if (strG[0] != 'M' && strG[0] != 'F')
	{
		char err[140] = "PN0014!R!invalid gender code : ";
		strcat( err, strG);
		strcat( err, " not F or M");
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}

	return retVal;

}
//2500-validateFNI
int CAssignPNObject::validateFNI( char* strFNI, char* strSSN )
{
	int retVal = 0;

	if (strFNI[0] != 'N' && strFNI[0] != 'Y')
	{
		char err[140] = "PN0015!R!force option ";
		strcat( err, strFNI);
		strcat( err, " not Y or N");
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}
	else if ( strFNI[0] == 'Y' && (strcmp(strSSN, "") != 0))
	{
		char err[140] = "PN0017!R!force option of yes not allowed is SSN specified";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 8;
	}
	return retVal;
}

//************************************************************************
//************************************************************************
//******************  Assign The Number       routines  ******************
//************************************************************************
//************************************************************************
//3000-assignTheNumber
int CAssignPNObject::assignTheNumber(char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
	int isPersonThere = 0;
	long personNumber = 0;
	char strPersonNumber[9] = "";
	char ForcedSeqNum[6] = "0";


	// All the LDAP related variables and constants
	LDAP   *ld; 	// handle to the directory
	int     rc;		//  return code for calling some LDAP API functions
	// user and password for binding to the directory
	char *dn =  "cn=password, ou=zzz, ou=qqq, o=acme inc, c=us";
	char *pw = "mypw";

	// Get a handle to an LDAP connection.
	if ( (ld = ldap_init( "serverBob", port100 )) == NULL )
	{
		char *err = "PN0001!R!directory not available 3000-init";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		return 12;
	}
	// Bind to the LDAP server.
	rc = ldap_simple_bind_s( ld, dn, pw );

	if ( rc != LDAP_SUCCESS )
	{
		char *err = "PN0001!R!directory not available3000 bind";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		return 12;
	}

	// We change a NULL SSN to zero.  LDAP needs a dummy value
	if (strcmp(strSSN, "") == 0)
		strcpy(strSSN, "0");

	// if the return code, isPersonThere = 4, this is just a warning and we should proceed to
	// create a new PN and add the entry to the tree.
	isPersonThere = CheckForPersonExistence(ld, strSSN,  strFN, strMI, strLN, strBD, strG,
									strFNI, ForcedSeqNum);

	// if return = 4 - warning msgs, but proceed with add
	//             6 - warning msgs, return existing PN - No new entry
	//			   7 - warning msgs, no number returned	- No new entry

	if (isPersonThere < 5)
	{
		// When adding an entry, a struct of type LDAPMod must exist for each attribute of the entry.
		// Each struct contains a 2 element array containing the attr value and a NULL.
		// (This is 2 elements because the personNumber attribs are all single value.)

		// if no SSN and FNI = Y, we don't write and ssn attrib but we do write an Forced Seq#

		char *bbSsnCode_vals[2];
		char *givenName_vals[2];
		char *initials_vals[2];
		char *sn_vals[2];
		char *bbBirthDate_vals[2];
		char *bbGender_vals[2];
		char *bbForceNumberIndicator_vals[2];
		char *uid_vals[2];
		char *bbForceSequenceNumber_vals[2];

		// Now declare the LDAPMod structures, one for each attribute
		LDAPMod uidMod,
				snMod,
				givenNameMod,
				initialsMod,
				bbSsnCodeMod,
				bbGenderMod,
				bbBirthDateMod,
				bbForceNumberIndicatorMod,
				bbForceSequenceNumberMod,
				ocMod;						 // the object class attribute

		// Now declare the array of pointers to the LDAPMod structures
		// This is what is passed as a parm to the LDAP ADD API
		LDAPMod *mods[11];
		for (int i=0; i<11; i++)
			mods[i] = NULL;

		// now generate a new person number
		personNumber = generatePersonNumber(strLN);
		m_PersonNumber = personNumber;			// Place it into the member variable

		//  Convert the person number to a string
		convertPNToString(personNumber, strPersonNumber);

		// The new DN is passed to the LDAP add call. For the person number object it looks like :
		// "uid=12345678, ou=Person Number, o=Acme Inc, c=us"
		// assuming the person number is 12345678.
		//  Since the person number, ie. uid,  has not yet been calculated, we split the string
		// in two here for later inclusion of the uid with the strcat function
		char newDN[57] = "";
		char newDNPartOne[5] = "uid=";
		char newDNPartTwo[44] = ", ou=Person Number, o=acme Inc, c=us";

		// The LDAP API add call needs an entry DN, which we can create now that we have a
		// uid, aka person number, as the uid is part of the DN (distinguished name)
		strcpy(newDN, newDNPartOne);
		strcat(newDN, strPersonNumber);
		strcat(newDN, newDNPartTwo);


		// Put the value of each attribute into it's array.
		// SSN

		bbSsnCode_vals[0] = strSSN;
		bbSsnCode_vals[1] = NULL;

		// first name - given name
		givenName_vals[0] = strFN;
		givenName_vals[1] = NULL;

		// middle initial
		initials_vals[0] = strMI;
		initials_vals[1] = NULL;

		// sn - sur name - last name
		sn_vals[0] = strLN;
		sn_vals[1] = NULL;

		// birth date
		bbBirthDate_vals[0] = strBD;
		bbBirthDate_vals[1] = NULL;

		// gender
		bbGender_vals[0] = strG;
		bbGender_vals[1] = NULL;

		// force number indicator
		bbForceNumberIndicator_vals[0] = strFNI;
		bbForceNumberIndicator_vals[1] = NULL;

		// forced sequence number
		bbForceSequenceNumber_vals[0] = ForcedSeqNum;
		bbForceSequenceNumber_vals[1] = NULL;

		//uid - person number
		uid_vals[0] = strPersonNumber;
		uid_vals[1] = NULL;

		// declare the object class level array using hardcoded initialization data
		char *oc_vals[] = { "top" , "bbPersonNumber", NULL };

		// Now fill in the values in each LDAPMod structure.  The mod_op is TO ADD AN ENTRY.
		// The mod_type is the type of attribute that particular LDAPMod struct is for.
		// mod_valuse is the 2 dimensional array for each attribute
		ocMod.mod_op = 0;
		ocMod.mod_type = "objectclass";
		ocMod.mod_values = oc_vals;

		uidMod.mod_op = 0;
		uidMod.mod_type = "uid";
		uidMod.mod_values = uid_vals;

		snMod.mod_op = 0;
		snMod.mod_type = "sn";
		snMod.mod_values = sn_vals;

		givenNameMod.mod_op = 0;
		givenNameMod.mod_type = "givenname";
		givenNameMod.mod_values = givenName_vals;

		bbSsnCodeMod.mod_op = 0;
		bbSsnCodeMod.mod_type = "bbSsnCode";
		bbSsnCodeMod.mod_values = bbSsnCode_vals;

		initialsMod.mod_op = 0;
		initialsMod.mod_type = "initials";
		initialsMod.mod_values = initials_vals;

		bbGenderMod.mod_op = 0;
		bbGenderMod.mod_type = "bbGender";
		bbGenderMod.mod_values = bbGender_vals;

		bbBirthDateMod.mod_op = 0;
		bbBirthDateMod.mod_type = "bbBirthDate";
		bbBirthDateMod.mod_values = bbBirthDate_vals;

		bbForceNumberIndicatorMod.mod_op = 0;
		bbForceNumberIndicatorMod.mod_type = "bbForceNumberIndicator";
		bbForceNumberIndicatorMod.mod_values = bbForceNumberIndicator_vals;

		bbForceSequenceNumberMod.mod_op = 0;
		bbForceSequenceNumberMod.mod_type = "bbForceSequenceNumber";
		bbForceSequenceNumberMod.mod_values = bbForceSequenceNumber_vals;


			mods[0] = &uidMod,
		mods[1] = &snMod,
		mods[2] = &givenNameMod,
		mods[3] = &bbSsnCodeMod,
		//mods[4] = &initialsMod,
		mods[4] = &bbGenderMod,
		mods[5] = &bbBirthDateMod,
		mods[6] = &bbForceNumberIndicatorMod,
		mods[7] = &bbForceSequenceNumberMod,
		mods[8] = &ocMod;
		//mods[10] = NULL;
		// mi is optional, so if it's blank or null dont send LDAP a mod struct for the mi attrib
		// if you send something such as a blank or null, LDAP complains
		if ( strcmp( strMI, "") != 0 && strcmp( strMI, " ") != 0)
		{
			initials_vals[0] = strMI;
			initials_vals[1] = NULL;
			initialsMod.mod_op = 0;
			initialsMod.mod_type = "initials";
			initialsMod.mod_values = initials_vals;
			mods[9] = &initialsMod,
			mods[10] = NULL;
		}



		// Perform the add operation.
		int returnAdd = 68;  // init to 68 which is the LDAP ret val for a dup entry
		while (returnAdd == 68)  // while ret val = dup, try again to add
		{
			returnAdd = addEntry(ld, newDN, mods);
			if (returnAdd == 68)  // duplicate
			{
				// the last generated pn was a duplicate - rare occurrence.
				//  make a new number - new distinguished name string
				//  put the number into the mod array
				personNumber = generatePersonNumber(strLN);
				//personNumber = personNumber + 9;  //old way just bump by 9 and try again
				convertPNToString(personNumber, strPersonNumber);
				strcpy(newDN, newDNPartOne);
				strcat(newDN, strPersonNumber);
				strcat(newDN, newDNPartTwo);
				m_PersonNumber = personNumber;	// Place it into the member variable
				//  Place it in its 2 dimensional array
				uid_vals[0] = strPersonNumber;
				uid_vals[1] = NULL;
			}					// end goodAdd if
			else if (returnAdd !=0)
			{
				// had a problem.  Create a message and set goodAdd = 0 to get out of loop.
				// To include the return code (goodAdd) in the message, convert it to a string.
				// Ret codes will be 1 or 2 digits.  If the code is a single digit, say 7, ecvt will
				// put 70 in the returned string.  So convert the string back to an int to make
				// sure it still = the original number.  If it's 10 times the orig, we know it was
				// a single digit, so lop off that extra 0 in the string.
				char* strRetCode;
				int  decimal_spot, sign;
				// find out how many digits the ret code is for the ecvt function call
				int y;
				int x = returnAdd / 10;   // string back to an int
				if (x < 1)
				{
					y = 1;
				}
				else if (x < 10)
				{
					y = 2;
				}
				else if (x < 100)
				{
					y = 3;
				}
				strRetCode = ecvt (returnAdd, y, &decimal_spot, &sign); // int to string 2 chars long
				char err[140] = "PN0001!R!Directory not available.  Return code from LDAP ADD operation : ";
				strcat( err, strRetCode );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				returnAdd = 12;		// lets not loop forever
			}

		}						// end goodAdd while

	}							// end personThere if
	ldap_unbind_s( ld );

	return isPersonThere;
}								//  end assign function





//3100-CheckForPersonExistence
//  Decide whether to continue with a new assign or return an existing PN.
int CAssignPNObject::CheckForPersonExistence( LDAP * ld, char* strSSN, char* strFN,
						 char* strMI,char* strLN,
						char* strBD, char* strG, char* strFNI, char* ForcedSeqNum)
{
	// 2 possibilities here.
	//If an SSN was input as a parm, see if there's a match in the person number
	//tree already.  If not, go back and add this new person.  If so:
	//        -
	int isPersonThere = 0;
	if ( strcmp(strSSN, "0" ) != 0)
	{
		isPersonThere = CheckForPersonExistenceWithSSN( ld, strSSN, strFN, strMI, strLN, strBD, strG, strFNI );
	}
	else
	{
		//isPersonThere = 666;
		isPersonThere = CheckForPersonExistenceWithoutSSN(ld, strSSN, strFN, strMI, strLN, strBD, strG, strFNI, ForcedSeqNum);
	}
	return isPersonThere;
}
//3110-CheckForPersonExistenceWithSSN
// Fisrt see if there's an existing entry with the same ssn.
// If there is not, check to see if there's an entry with any SSN and all
//		of the remaining attributes.  If this 'close match' is found,(all but SSN),
//		return a a warning message, but this program will still assign a new Person
//		number and add an entry to the PN tree.
// If there is one SSN match found, (later in this program, a new PN is not generated
//		nor will an new entry be made).
//		Then check the matched (existing) entry's attribs to see if
//		how closely they match.  For each attrib, if it doesnt match the input attribs,
//		generate a warning message to inform the user that an anomoly condition
//		may exist.
// If more than one SSN match is found, loop through match and just create a warning
//		message for each one.
int CAssignPNObject::CheckForPersonExistenceWithSSN( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{

	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int retVal = 0;
	char storedPN[9] = "";
	int  rc, num_entries = 0, num_refs = 0;
	int isPersonThere = 0;
	struct attributes				// struct to hold the attribute vals for
	{								// an entry that's almost a duplicate
		char storedSSN[10];
		char storedFN[31];
		char storedMI[31];
		char storedLN[31];
		char storedBD[31];
		char storedG[31];
		char storedFNI[31];
	} storedPersonAttributes;

	struct attributes* ptr_storedPerson;
	ptr_storedPerson = &storedPersonAttributes;
	char strFilter[200] = "";
	strcat( strFilter, "(bbSsnCode=");
	strcat( strFilter, strSSN);
	strcat( strFilter, ")");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char *err = "PN0001!R!directory not available -chk for exist w ssn ";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;				// bad return code - trouble with the ldap call
	}
	else
	{
		num_entries = ldap_count_entries( ld, result );  // any hits?

		if (num_entries == 0)
		{
			// no other entry with this ssn.  Now check for one with all attrs but ssn.
			// Note if there is one, a warning is generated, but generation of
			// a new PN continues.
			int closeOne = 0;
			closeOne = CheckForPersonExistenceWithSSNWildCard(ld, strSSN, strFN,
					strMI,strLN,strBD,strG, strFNI );
			retVal = closeOne;
		}
		else if (num_entries == 1)
		{
			retVal = 6;
			int mismatchFlag = 0;
			e = ldap_first_entry( ld, result );
			 // Iterate through each attribute in the entry.
			for ( a = ldap_first_attribute( ld, e, &ber );
								a != NULL; a = ldap_next_attribute( ld, e, ber ) )
			{
				// For each attribute, print the attribute name and values.
				// All single val attributes, so use vals[0]
				if ((vals = ldap_get_values( ld, e, a)) != NULL )
				{
					if (strcmp(a, "uid") == 0)
					{
						strcpy(storedPN, vals[0]);
 						m_PersonNumber = atol(storedPN);
					}
					else
					if (strcmp(a, "givenName") == 0)
					{
						if (strcmp(strFN, vals[0] ) != 0)
						{
							char err[140] = "PN0019!W!SSN match, first name mismatch. Input : ";
							strcat( err, strFN );
							strcat( err, " stored : " );
							strcat( err, vals[0] );
							strcat( err, " , existing PN returned" );
							m_msgQuantity ++;
							m_vecMsgs.push_back(CComBSTR(err));
							mismatchFlag = 1;
						}
					}
					else
					if (strcmp(a, "initials") == 0)
					{
						if (strcmp(strMI, vals[0] ) != 0)
						{
							char err[140] = "PN0021!W!SSN match, middle initial mismatch. Input : ";
							strcat( err, strMI );
							strcat( err, " stored : " );
							strcat( err, vals[0] );
							strcat( err, " , existing PN returned" );
							m_msgQuantity ++;
							m_vecMsgs.push_back(CComBSTR(err));
							mismatchFlag = 1;
						}
					}
					else
					if (strcmp(a, "sn") == 0)
					{
						if (strcmp(strLN, vals[0] ) != 0)
						{
							char err[140] = "PN0020!W!SSN match, last name mismatch. Input : ";
							strcat( err, strLN );
							strcat( err, " stored : " );
							strcat( err, vals[0] );
							strcat( err, " , existing PN returned" );
							m_msgQuantity ++;
							m_vecMsgs.push_back(CComBSTR(err));
							mismatchFlag = 1;
						}
					}
					else
					if (strcmp(a, "bbGender") == 0)
					{
						if (strcmp(strG, vals[0] ) != 0)
						{
							char err[140] = "PN0023!W!SSN match, gender mismatch. Input : ";
							strcat( err, strG );
							strcat( err, " stored : " );
							strcat( err, vals[0] );
							strcat( err, " , existing PN returned" );
							m_msgQuantity ++;
							m_vecMsgs.push_back(CComBSTR(err));
							mismatchFlag = 1;
						}
					}
					else
					if (strcmp(a, "bbBirthDate") == 0)
					{
						if (strcmp(strBD, vals[0] ) != 0)
						{
							char err[140] = "PN0022!W!SSN match, birth date mismatch. Input : ";
							strcat( err, strBD );
							strcat( err, " stored : " );
							strcat( err, vals[0] );
							strcat( err, " , existing PN returned" );
							m_msgQuantity ++;
							m_vecMsgs.push_back(CComBSTR(err));
							mismatchFlag = 1;
						}
					}  // end big if else - one if for each possible attribute
				}	// end the loop through the attributes
				ldap_value_free( vals );
			}  // end of looping through the entries

			ldap_memfree( a );
			if (mismatchFlag == 0)
			{
				mismatchFlag = 0;
				char err[140] = "PN0018!W!All attributes match an exisiting entry. Person Number : ";
				strcat( err, storedPN );
				strcat( err, " , existing PN returned" );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
			}
		}
		else if (num_entries > 1)
		{
			retVal = 7;
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // Iterate through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Produce a message containing the matched entry's person number
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							char err[140] = "PN0030!R!For SSN :";
							strcat( err, strSSN );
							strcat( err, " more than one person already existing. Existing PN : " );
							strcat( err, vals[0] );    // single value attribute : vals[0]
							m_msgQuantity ++;
							m_vecMsgs.push_back(CComBSTR(err));
							retVal = 12;
						}

					}		// end the loop through entry - one loop per attribute
					ldap_value_free( vals );
				}					// end of looping through the attributes
			ldap_memfree( a );
			}  // end of for looping through the entries - go to for loop top to get next entry
		}			// end of if els

		if ( ber != NULL )
			ber_free( ber, 0 );
		ldap_msgfree( result );
   	}

	return retVal;				// bad return code - trouble with the ldap call
}

//3120-CheckForPersonExistenceWithoutSSN
int CAssignPNObject::CheckForPersonExistenceWithoutSSN( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI, char* ForcedSeqNum)
{
	int isPersonThere = 0;
	isPersonThere = CheckForPersonExistenceWithAllNonSSNAttributes(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI, ForcedSeqNum);
	if (isPersonThere == 0)
	{
		isPersonThere = CheckForPersonExistenceWithSSNWildCard(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI);
		isPersonThere = CheckForPersonExistenceWithFirstNameWildCard(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI);
		isPersonThere = CheckForPersonExistenceWithMiddleIWildCard(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI);
		isPersonThere = CheckForPersonExistenceWithLastNameWildCard(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI);
		isPersonThere = CheckForPersonExistenceWithBirthDateWildCard(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI);
		isPersonThere = CheckForPersonExistenceWithGenderWildCard(ld, strSSN, strFN,
				strMI,strLN,strBD,strG, strFNI);
	}
	return isPersonThere;



}
//3121-CheckForPersonExistenceWithAllNonSSNAttributes
int CAssignPNObject::CheckForPersonExistenceWithAllNonSSNAttributes( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI, char* ForcedSeqNum)
{
	// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedForcedSeqNum[6] = "";
	char storedPN[9] = "";
	int iSeqNum = 0;
	int prevSeqNum = 0;
	long lSeqNum = 0;

	char storedSSN[10] = "";


	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int       rc, num_entries;

	int retVal = 0;
	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode=0)");
	strcat( strFilter, "(givenName=");
	strcat( strFilter, strFN);
	strcat( strFilter, ")(initials=");
	strcat( strFilter, strMI);
	strcat( strFilter, ")(sn=");
	strcat( strFilter, strLN);
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, strG);
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, strBD);
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	//char err[150] = "BB in CheckForPersonExistenceWithAllNonSSNAttributes TOP  ";
	//strcat( err, strFilter);
	//m_msgQuantity ++;
	//m_vecMsgs.push_back(CComBSTR(err));

	if (rc != 0)
	{
		char *err = "PN0001!R!directory not available -chk for exist w ssn ";
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;				// bad return code - trouble with the ldap call
	}
	else
	{
		num_entries = ldap_count_entries( ld, result );  // any hits?

		if (num_entries == 0)
		{
			if (strcmp(strFNI, "Y") == 0)
			{
				char err[140] = "PN0016!R!Force option of yes specified, but no matching person found. No PN returned.";
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				retVal = 8;
			}
			else
				retVal = 0;
		}
		else if(num_entries == 1)
		{
			// first get the vals of the uid and StoredForce Number attributes
			e = ldap_first_entry( ld, result );
			 // Iterate through each attribute in the entry.
			for ( a = ldap_first_attribute( ld, e, &ber );
								a != NULL; a = ldap_next_attribute( ld, e, ber ) )
			{
				// For each attribute, print the attribute name and values.
				// All single val attributes, so use vals[0]
				if ((vals = ldap_get_values( ld, e, a)) != NULL )
				{
					if (strcmp(a, "uid") == 0)
					{
						strcpy(storedPN, vals[0]);
					}
					else
					if (strcmp(a, "bbForceSequenceNumber") == 0)
					{
						strcpy(storedForcedSeqNum, vals[0]);
						iSeqNum = atoi(storedForcedSeqNum);
					}
				}	// end the loop through the attributes - one loop per attribute
				ldap_value_free( vals );
			}  // end of looping through the entries
			ldap_memfree( a );

			if (strcmp(strFNI, "N") == 0)
			{
				retVal = 7;
				m_PersonNumber = atol(storedPN); // return the stored number don't force a new one
				char err[150] = "PN0032!W!For";
				strcat(err, strSSN);strcat(err, " ");
				strcat(err, strFN);strcat(err, " ");
				strcat(err, strMI);strcat(err, " ");
				strcat(err, strLN);strcat(err, " ");
				strcat(err, strBD);strcat(err, " ");
				strcat(err, strG);
				strcat(err, "one PN ");
				strcat(err, storedPN);
				strcat(err, " found. Returning stored PN.");
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
			}
			else
			{
				iSeqNum = (atoi(storedForcedSeqNum));
				iSeqNum++;
				int y;
				int x = iSeqNum / 10;   // string back to an int
				if (x < 1)   // was it a 1 or 2 digit return code
				{
					y = 1;	// it was 1 digit, so lop off the 10's place
				}
				else if (x < 10)   // was it a 1 or 2 digit return code
				{
					y = 2;
				}
				else if (x < 100)   // was it a 1 or 2 digit return code
				{
					y = 3;
				}
				else if (x < 1000)   // was it a 1 or 2 digit return code
				{
					y = 4;
				}
				int  decimal_spot, sign;  // These end up being throw aways
				char *pn;
				pn = ecvt (iSeqNum, y, &decimal_spot, &sign);
				strcpy(ForcedSeqNum, pn);
				retVal = 0;  // go ahead and create a new person number



				//char err[150] = "BB found 1 match FNI = Y , stored seq num = ";
				//strcat(err, storedForcedSeqNum );
				//strcat( err, " sending back val of ");
				//strcat(err, ForcedSeqNum );
				//m_msgQuantity ++;
				//m_vecMsgs.push_back(CComBSTR(err));
			}
		}
		else if (num_entries > 1)
		{
			//char err[150] = "BB in CheckForPersonExistenceWithAllNonSSNAttributes NE > 1  ";
			//m_msgQuantity ++;
			//m_vecMsgs.push_back(CComBSTR(err));

			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // Iterate through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Produce a message containing the matched entry's person number
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy(storedPN, vals[0]);
 							//m_PersonNumber = atol(storedPN);
						}
						else
						if (strcmp(a, "bbForceSequenceNumber") == 0)
						{
							strcpy(storedForcedSeqNum, vals[0]);
							iSeqNum = atoi(storedForcedSeqNum);

						}
					}
				}		// end the loop through the attributes

				ldap_value_free( vals );
				if (strcmp(strFNI, "N") == 0)
				{
					char err[150] = "PN0031!R!For ";
					strcat(err, strFN);strcat(err, " ");
					strcat(err, strMI);strcat(err, " ");
					strcat(err, strLN);strcat(err, " ");
					strcat(err, strBD);strcat(err, " ");
					strcat(err, strG);
					strcat(err, ", more than one PN ");
					strcat(err, storedPN);
					strcat(err, " found. No PN will be returned. ");
					m_msgQuantity ++;
					m_vecMsgs.push_back(CComBSTR(err));
					retVal = 7;
				}
				else
				{
					// We'll force a new number so get the largest FseqNum value
					// We'll increment it one more for this new entry
					if (iSeqNum > prevSeqNum)
						prevSeqNum = iSeqNum;

					//char err[150] = "BB Loop found > 1 match FNI = Y , stored seq num = ";
					//strcat(err, storedForcedSeqNum );
					//strcat( err, "sending back val of ");
					//strcat(err, ForcedSeqNum );
					//m_msgQuantity ++;
					//m_vecMsgs.push_back(CComBSTR(err));
				}

			}	// end of looping through the entries
			ldap_memfree( a );

			if (strcmp(strFNI, "Y") == 0)
			{
				// convert the ForcedSeqNum to a string for the new add
				iSeqNum++;
				int y;
				int x = iSeqNum / 10;   // string back to an int
				if (x < 1)   // was it a 1 or 2 digit return code
				{
					y = 1;	// it was 1 digit, so lop off the 10's place
				}
				else if (x < 10)   // was it a 1 or 2 digit return code
				{
					y = 2;
				}
				else if (x < 100)   // was it a 1 or 2 digit return code
				{
					y = 3;
				}
				else if (x < 1000)   // was it a 1 or 2 digit return code
				{
					y = 4;
				}
				int  decimal_spot, sign;  // These end up being throw aways
				char *pn;
				pn = ecvt (iSeqNum, y, &decimal_spot, &sign);
				strcpy(ForcedSeqNum, pn);
				retVal = 0;  // go ahead and create a new person number

				//char err[150] = "BB Final found > 1 match FNI = Y , stored seq num = ";
				//strcat(err, storedForcedSeqNum );
				//strcat( err, "sending back val of ");
				//strcat(err, ForcedSeqNum );
				//m_msgQuantity ++;
				//m_vecMsgs.push_back(CComBSTR(err));
			}

		}			// end of if num_entry elses

		if ( ber != NULL )
			ber_free( ber, 0 );
		ldap_msgfree( result );
   	}

	return retVal;
}



//3122-CheckForPersonExistenceWithSSNWildCard
// Looks for an entry with all the same attributes (except ssn), equal to those of the person
// for which a new PN is being asked for - ie the input params to AssignNumber.
int CAssignPNObject::CheckForPersonExistenceWithSSNWildCard( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
	// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedPN[9] = "";
	char storedSSN[11] = "";
	int retVal = 0;
	// LDAP Variables
	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int rc, num_entries;


	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode="); // ssn=* is the same as not including ssn in the filter
	strcat( strFilter, "*");
	strcat( strFilter, ")(givenName=");
	strcat( strFilter, strFN);
	if ( strcmp( strMI, "") != 0 && strcmp( strMI, "") != 0 )
	{
		strcat( strFilter, ")(initials=");
		strcat( strFilter, strMI);
	}
	strcat( strFilter, ")(surname=");
	strcat( strFilter, strLN);
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, strG);
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, strBD);
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char err[150] = "PN0001!R!directory unavailable. SSN=* Filter : ";
		strcat( err, strFilter);
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;
	}
	else
	{
		num_entries = ldap_count_entries( ld, result );
		if (num_entries == 0)
			retVal = 0;
		else
		{
			retVal = 4;   //  got a close match but ret val is still 0 - just a warning
			// loop through each entry in the result set
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // loop through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Save the relevant attributes in local variables
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy( storedPN, vals[0]);
						}
						if (strcmp(a, "bbSsnCode") == 0)
						{
							strcpy( storedSSN, vals[0]);
						}

					}		// end the loop through entry - one loop per attribute


				}					// end of looping through the attributes
				ldap_value_free( vals );
				ldap_memfree( a );
				char err[150] = "PN0029!W!possible PN duplicate. SSN is only mismatch. Stored PN :";
				strcat( err, storedPN );    // single value attribute : vals[0]
				strcat( err, " Stored SSN : ");
				strcat( err, storedSSN );
				strcat( err, " Input SSN : ");
				strcat( err, strSSN );
				m_vecMsgs.push_back(CComBSTR(err));
				m_msgQuantity ++;
			}  // end of for looping through the entry - go to for loop top to get next entry

		}
	}

	return retVal;
}


//3123-CheckForPersonExistenceWithFirstNameWildCard
int CAssignPNObject::CheckForPersonExistenceWithFirstNameWildCard( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
	// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedPN[9] = "";
	char storedGivenName[31] = "";
	int retVal = 0;
	// LDAP Variables
	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int rc, num_entries;

	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode=");
	strcat( strFilter, strSSN);
	strcat( strFilter, ")(givenName=");
	strcat( strFilter, "*");
	if ( strcmp( strMI, "") != 0 && strcmp( strMI, "") != 0 )
	{
		strcat( strFilter, ")(initials=");
		strcat( strFilter, strMI);
	}
	strcat( strFilter, ")(surname=");
	strcat( strFilter, strLN);
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, strG);
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, strBD);
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char err[150] = "PN0001!R!directory unavailable FN=* Filter : ";
		strcat( err, strFilter);
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;
	}
	else
	{

		num_entries = ldap_count_entries( ld, result );
		if (num_entries == 0)
			retVal = 0;
		else
		{
			retVal = 4;   //  got a close match but ret val is still 0 - just a warning
			// loop through each entry in the result set
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // loop through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Save the relevant attributes in local variables
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy( storedPN, vals[0]);
						}
						if (strcmp(a, "givenName") == 0)
						{
							strcpy( storedGivenName, vals[0]);
						}

					}		// end the loop through entry - one loop per attribute

				}					// end of looping through the attributes
				ldap_value_free( vals );
				ldap_memfree( a );
				char err[150] = "PN0025!W!possible PN duplicate. First Name is only mismatch. Stored PN :";
				strcat( err, storedPN );    // single value attribute : vals[0]
				strcat( err, ", Stored First Name : ");
				strcat( err, storedGivenName );
				strcat( err, ", Input First Name : ");
				strcat( err, strFN );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));

			}  // end of for looping through the entries - go to for loop top to get next entry

		}
	}

	return retVal;
}
//3124-CheckForPersonExistenceWithMiddleIWildCard
int CAssignPNObject::CheckForPersonExistenceWithMiddleIWildCard( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
		// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedPN[9] = "";
	char storedMI[2] = "";
	int retVal = 0;
	// LDAP Variables
	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int rc, num_entries;

	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode=");
	strcat( strFilter, strSSN);
	strcat( strFilter, ")(givenName=");
	strcat( strFilter, strFN);
	strcat( strFilter, ")(initials=");
	strcat( strFilter, "*");
	strcat( strFilter, ")(surname=");
	strcat( strFilter, strLN);
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, strG);
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, strBD);
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char err[150] = "PN0001!R!directory unavailable. MI=* Filter : ";
		strcat( err, strFilter);
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;
	}
	else
	{

		num_entries = ldap_count_entries( ld, result );
		if (num_entries == 0)
			retVal = 0;
		else
		{
			retVal = 4;   //  got a close match but ret val is still 0 - just a warning
			// loop through each entry in the result set
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // loop through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Save the relevant attributes in local variables
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy( storedPN, vals[0]);
						}
						if (strcmp(a, "initials") == 0)
						{
							strcpy( storedMI, vals[0]);
						}

					}		// end the loop through entry - one loop per attribute


				}					// end of looping through the attributes
				char err[150] = "PN0024!W!possible PN duplicate. Middle Initial is only mismatch. Stored PN :";
				strcat( err, storedPN );    // single value attribute : vals[0]
				strcat( err, " Stored Middle Initial : ");
				strcat( err, storedMI );
				strcat( err, " Input Middle Initial : ");
				strcat( err, strMI );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				ldap_value_free( vals );
				ldap_memfree( a );
			}  // end of for looping through the entries - go to for loop top to get next entry

		}
	}

	return retVal;

}
//3125-CheckForPersonExistenceWithLastNameWildCard
int CAssignPNObject::CheckForPersonExistenceWithLastNameWildCard( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
		// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedPN[9] = "";
	char storedLN[31] = "";
	int retVal = 0;
	// LDAP Variables
	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int rc, num_entries;

	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode=");
	strcat( strFilter, strSSN);
	strcat( strFilter, ")(givenName=");
	strcat( strFilter, strFN);
	if ( strcmp( strMI, "") != 0 && strcmp( strMI, "") != 0 )
	{
		strcat( strFilter, ")(initials=");
		strcat( strFilter, strMI);
	}
	strcat( strFilter, ")(surname=");
	strcat( strFilter, "*");
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, strG);
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, strBD);
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char err[150] = "PN0001!R!directory unavailable LN=* Filter : ";
		strcat( err, strFilter);
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;
	}
	else
	{

		num_entries = ldap_count_entries( ld, result );
		if (num_entries == 0)
			retVal = 0;
		else
		{
			retVal = 4;   //  got a close match but ret val is still 0 - just a warning
			// loop through each entry in the result set
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // loop through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Save the relevant attributes in local variables
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy( storedPN, vals[0]);
						}
						if (strcmp(a, "sn") == 0)
						{
							strcpy( storedLN, vals[0]);
						}

					}		// end the loop through entry - one loop per attribute


				}					// end of looping through the attributes
				char err[150] = "PN0026!W!possible PN duplicate. Last Name is only mismatch. Stored PN :";
				strcat( err, storedPN );    // single value attribute : vals[0]
				strcat( err, " Stored Last Name : ");
				strcat( err, storedLN );
				strcat( err, " Input Last Name : ");
				strcat( err, strLN );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));
				ldap_value_free( vals );
				ldap_memfree( a );
			}  // end of for looping through the entries - go to for loop top to get next entry

		}
	}

	return retVal;

}
//3126-CheckForPersonExistenceWithBirthDateWildCard
int CAssignPNObject::CheckForPersonExistenceWithBirthDateWildCard( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
		// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedPN[9] = "";
	char storedBD[10] = "";
	int retVal = 0;
	// LDAP Variables
	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int rc, num_entries;

	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode=");
	strcat( strFilter, strSSN);
	strcat( strFilter, ")(givenName=");
	strcat( strFilter, strFN);
	if ( strcmp( strMI, "") != 0 && strcmp( strMI, "") != 0 )
	{
		strcat( strFilter, ")(initials=");
		strcat( strFilter, strMI);
	}
	strcat( strFilter, ")(surname=");
	strcat( strFilter, strLN);
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, strG);
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, "*");
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char err[150] = "PN0001!R!directory unavailable BD=* Filter : ";
		strcat( err, strFilter);
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;
	}
	else
	{

		num_entries = ldap_count_entries( ld, result );
		if (num_entries == 0)
			retVal = 0;
		else
		{
			retVal = 4;   //  got a close match but ret val is still 0 - just a warning
			// loop through each entry in the result set
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // loop through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Save the relevant attributes in local variables
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy( storedPN, vals[0]);
						}
						if (strcmp(a, "bbBirthDate") == 0)
						{
							strcpy( storedBD, vals[0]);
						}

					}		// end the loop through entry - one loop per attribute

				}			// end of looping through the attributes
				ldap_value_free( vals );
				ldap_memfree( a );
				char err[150] = "PN0027!W!possible PN duplicate. Birth date is only mismatch. Stored PN :";
				strcat( err, storedPN );    // single value attribute : vals[0]
				strcat( err, " Stored Birth Date : ");
				strcat( err, storedBD );
				strcat( err, " Input Birth Date : ");
				strcat( err, strBD );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));

			}  // end of for looping through the entries - go to for loop top to get next entry

		}
	}

	return retVal;
}
//3127-CheckForPersonExistenceWithGenderWildCard
int CAssignPNObject::CheckForPersonExistenceWithGenderWildCard( LDAP * ld, char* strSSN, char* strFN,char* strMI,char* strLN,
				char* strBD,char* strG,char* strFNI)
{
		// To search for an entry based on all the non SSN arributes, we need a search filter that looks like
	//"(&(givenName=johnny)(initials=jj)(surname=johnson)(bbGender=M)(bbBirthDate=10/31/22))"
	char storedPN[9] = "";
	char storedG[2] = "";
	int retVal = 0;
	// LDAP Variables
	LDAPMessage   *result, *e;
	BerElement   *ber;
	char   *a;
	char   **vals;
	int rc, num_entries;

	char strFilter[200] = "";
	strcat( strFilter, "(&(bbSsnCode=");
	strcat( strFilter, strSSN);
	strcat( strFilter, ")(givenName=");
	strcat( strFilter, strFN);
	if ( strcmp( strMI, "") != 0 && strcmp( strMI, "") != 0 )
	{
		strcat( strFilter, ")(initials=");
		strcat( strFilter, strMI);
	}
	strcat( strFilter, ")(surname=");
	strcat( strFilter, strLN);
	strcat( strFilter, ")(bbGender=");
	strcat( strFilter, "*");
	strcat( strFilter, ")(bbBirthDate=");
	strcat( strFilter, strBD);
	strcat( strFilter, "))");
	rc = ldap_search_ext_s( ld, "ou=Person Number,o=Acme Inc,c=us",
							LDAP_SCOPE_SUBTREE,
							strFilter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
							LDAP_NO_LIMIT, &result );

	if (rc != 0)
	{
		char err[150] = "PN0001!R!directory unavailable G=* Filter : ";
		strcat( err, strFilter);
		m_msgQuantity ++;
		m_vecMsgs.push_back(CComBSTR(err));
		retVal = 12;
	}
	else
	{

		num_entries = ldap_count_entries( ld, result );
		if (num_entries == 0)
			retVal = 0;
		else
		{
			retVal = 4;   //  got a close match but ret val is still 0 - just a warning
			// loop through each entry in the result set
			for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) )
			{
				 // loop through each attribute in the entry.
				for ( a = ldap_first_attribute( ld, e, &ber );
									a != NULL; a = ldap_next_attribute( ld, e, ber ) )
				{
					// Save the relevant attributes in local variables
					if ((vals = ldap_get_values( ld, e, a)) != NULL )
					{
						if (strcmp(a, "uid") == 0)
						{
							strcpy( storedPN, vals[0]);
						}
						if (strcmp(a, "bbGender") == 0)
						{
							strcpy( storedG, vals[0]);
						}

					}		// end the loop through entry - one loop per attribute

				}					// end of looping through the attributes
				ldap_value_free( vals );
				ldap_memfree( a );
				char err[150] = "PN0028!W!possible PN duplicate. Gender is only mismatch. Stored PN :";
				strcat( err, storedPN );    // single value attribute : vals[0]
				strcat( err, " Stored Gender : ");
				strcat( err, storedG );
				strcat( err, " Input Gender : ");
				strcat( err, strG );
				m_msgQuantity ++;
				m_vecMsgs.push_back(CComBSTR(err));

			}  // end of for looping through the entries - go to for loop top to get next entry

		}
	}

	return retVal;

}




long CAssignPNObject::generatePersonNumber(char* ln)
{
	// Create a person number
	//  - Add the ASCII val of each character of the last name together
	//  - Get system time and seed the random generator with it
	//  - Get a random number and add it to the sum of chars from last name
	int  decimal_spot, sign;
	char strNewVal[9] = "";
	char * strVal = "";
	long personNumber = 0;
	int lgth = strlen(ln);
	int sumOfLetters = 0;
	int  randomVal;

    // get millisecs - next 3 lines
	int secsTime = 0;

	time_t ltime;
    time( &ltime );
	struct _timeb tstruct;

    secsTime = ltime;
	 _ftime( &tstruct );
	int millis = tstruct.millitm;

	for ( int i=0; i < lgth; i++)
	{
		sumOfLetters += (int)ln[i];
	};
	srand( millis );

	randomVal = (int)((double)rand() / ((double)RAND_MAX + 1) * secsTime);

	randomVal = sumOfLetters + randomVal;

	strVal = ecvt (randomVal, 8, &decimal_spot, &sign);  // convert val to a string

	int z = 0;

	for (i = 3; i < 8; i++, z++)
		strNewVal[z] = strVal[i];
	for ( i = 0; i < 4; i++, z++)
		strNewVal[z] = strVal[i];
	strNewVal[8] = '\0';

	personNumber = atol(strVal);

	return personNumber;
	/*
	long personNumber = 0;
	int lgth = strlen(ln);
	int sumOfLetters = 0;
	int timeVal, randomVal;
	for ( int i=0; i < lgth; i++)
	{
		sumOfLetters += (int)ln[i];
	};

	timeVal = (unsigned)time(0);  // 0 parm returns an int

	srand( timeVal );

	randomVal = rand();

	sumOfLetters = sumOfLetters + randomVal;

	personNumber = sumOfLetters + 70000000;

	return personNumber;
	*/
}

void CAssignPNObject::convertPNToString(long personNumber, char * strPersonNumber)
{
	// We have a person number in the form of a long but we need it in string form

	int  decimal_spot, sign;  // These end up being throw aways
	char *pn;

	pn = ecvt (personNumber, 8, &decimal_spot, &sign) ;

	strcpy(strPersonNumber, pn);

	//return 0;
}







int CAssignPNObject::addEntry(LDAP* ld, char* newDN, LDAPMod* mods [])
{

	// the ldap_add_ext_s returns a code indicating success or not
	// check it back in the calling function
	int rc;
	rc = ldap_add_ext_s( ld, newDN, mods, NULL, NULL );
	return rc;

}