#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include "portpriv.h"
#include "port.h"

// redefine dbcs versions of common string functions
#ifdef strnicmp
#undef strnicmp
#endif
#define strnicmp    My_mbsnicmp
#define strncmp     My_mbsncmp
#define strncpy     My_mbsncpy
#define strtok      My_mbstok

/* globals for this module */
HANDLE	  hMMFile = 0;
HANDLE	  hDLL;
BOOL	  bInit = FALSE;

/* function prototypes for private module functions */
void WINAPI FreePortData ();
int  WINAPI GetFirstToken (LPPORT *);
int  WINAPI GetNextToken (LPPORT *);
void WINAPI IgnoreToken (char *, LPPORT);
BOOL WINAPI LoadSection (char *, char *, DWORD, int *, char *);
BOOL WINAPI GetIniFile (HANDLE, char *, char *);

/****************************************************************************
    My_mbschr:  strchr() DBCS version
****************************************************************************/
unsigned char * _CRTAPI1 My_mbschr(
    unsigned char *psz, unsigned short uiSep)
{
    while (*psz != '\0' && *psz != uiSep) {
        psz = CharNext(psz);
    }
    if (*psz == '\0' && uiSep != '\0') {
        return NULL;
    } else {
        return psz;
    }
}
/****************************************************************************
    My_mbstok:  strtok() DBCS version
****************************************************************************/
unsigned char * _CRTAPI1 My_mbstok(
    unsigned char *pszSrc, unsigned char *pszSep)
{
    static char *pszSave = NULL;
    char *pszHead;
    char *psz;

    if (pszSrc == NULL) {
        if (pszSave == NULL) {
            return NULL;
        } else {
            psz = pszSave;
        }
    } else {
        psz = pszSrc;
    }

    /*********************************************/
    /* Skip delimiters to find a head of a token */
    /*********************************************/
    while (*psz) {
        if (IsDBCSLeadByte(*psz)) {
            break;
        } else if (NULL == My_mbschr(pszSep, *psz)) {
            break;
        }
        psz++;
    }
    if (*psz == '\0') {
        //No more token
        return (pszSave = NULL);
    }
    pszHead = psz;

    /******************************/
    /* Search a Tail of the token */
    /******************************/
    while (*psz) {
        if (IsDBCSLeadByte(*psz)) {
            psz += 2;
            continue;
        } else if (NULL != My_mbschr(pszSep, *psz)) {
            break;
        }
        psz++;
    }
    if (*psz == '\0') {
        pszSave = NULL;
    } else {
        //Found next delimiter
        pszSave = psz + 1;
        *psz = '\0';
    }
    return pszHead;
}
/****************************************************************************
    My_mbsnicmp:strnicmp() DBCS version
                If 'nLen' splits a DBC, this function compares
                the DBC's 2nd byte also.
****************************************************************************/
int _CRTAPI1 My_mbsnicmp(
    const unsigned char *psz1, const unsigned char *psz2, size_t Length)
{
    int nLen = (int)Length;

    while (0 < nLen) {
        if ('\0' == *psz1 || '\0' == *psz2) {
            return *psz1 - *psz2;
        }
        if (IsDBCSLeadByte(*psz1) || IsDBCSLeadByte(*psz2)) {
            if (*psz1 != *psz2 || *(psz1+1) != *(psz2+1)) {
                return *psz1 - *psz2;
            }
            psz1 += 2;
            psz2 += 2;
            nLen -= 2;
        } else {
            if((BYTE)CharUpper((LPSTR)*psz1) != (BYTE)CharUpper((LPSTR)*psz2)){
                return *psz1 - *psz2;
            }
            psz1++;
            psz2++;
            nLen--;
        }
    }
    return 0;
}
/****************************************************************************
    My_mbsncmp: strncmp() DBCS version
                If 'nLen' splits a DBC, this function compares
                the DBC's 2nd byte also.
****************************************************************************/
int _CRTAPI1 My_mbsncmp(
    const unsigned char *psz1, const unsigned char *psz2, size_t Length)
{
    int nLen = (int)Length;

    while (0 < nLen) {
        if ('\0' == *psz1 || '\0' == *psz2) {
            return *psz1 - *psz2;
        }
        if (IsDBCSLeadByte(*psz1) || IsDBCSLeadByte(*psz2)) {
            if (*psz1 != *psz2 || *(psz1+1) != *(psz2+1)) {
                return *psz1 - *psz2;
            }
            psz1 += 2;
            psz2 += 2;
            nLen -= 2;
        } else {
            if (*psz1 != *psz2) {
                return *psz1 - *psz2;
            }
            psz1++;
            psz2++;
            nLen--;
        }
    }
    return 0;
}
/****************************************************************************
    My_mbsncpy:
****************************************************************************/
unsigned char * _CRTAPI1 My_mbsncpy(
    unsigned char *psz1, const unsigned char *psz2, size_t Length)
{
    int nLen = (int)Length;
    unsigned char *pszSv = psz1;

    while (0 < nLen) {
        if (*psz2 == '\0') {
            *psz1++ = '\0';
            nLen--;
        } else if (IsDBCSLeadByte(*psz2)) {
            if (nLen == 1) {
                *psz1 = '\0';
            } else {
                *psz1++ = *psz2++;
                *psz1++ = *psz2++;
            }
            nLen -= 2;
        } else {
            *psz1++ = *psz2++;
            nLen--;
        }
    }
    return pszSv;
}

/* entry point for DLL loading and unloading */
BOOL WINAPI DllMain (
    HANDLE    hModule,
    DWORD     dwFunction,
    LPVOID    lpNot)
{
#ifdef DEBUG
DebugBreak ();
#endif

    switch (dwFunction)
	{
	case DLL_PROCESS_ATTACH:
		hDLL = hModule;
	    break;

	case DLL_PROCESS_DETACH:
	    FreePortData ();
	default:
	    break;
	}

    return TRUE;
}



/* function initializes port structures */
BOOL WINAPI InitPortData (
	char	  *szIniFileName
	)
{
    char	szSection[MAX_PATH];
    char	szIniFilePath[MAX_PATH];
    char	szMapFileName[MAX_PATH];
    OFSTRUCT	of;
    HANDLE	hFile;
    DWORD	nFileSize;
    int 	nOffset = 0;
    char	*lpMMFile;

	/* Initialize Flag off */
	bInit = FALSE;

    /* load name for global file mapping */
    LoadString (hDLL, IDS_MAPFILENAME, szMapFileName, MAX_PATH);

    /* after first process initializes port data */
    if ((hMMFile = OpenFileMapping (FILE_MAP_WRITE, FALSE, szMapFileName)))
	/* exit now since initialization was already performed by another process */
	return TRUE;

    /* retrive path and file for ini file */
	if (!GetIniFile (hDLL, szIniFileName, szIniFilePath))
	return FALSE;

    /* test for ini file existance and get length of file */
    if ((int)(hFile = (HANDLE)OpenFile (szIniFilePath, &of, OF_READ)) == -1)
	return FALSE;
    else
	{
	nFileSize = GetFileSize (hFile, NULL);
	CloseHandle (hFile);
	}

    /* allocate a segment of the swap file for shared memory 2*Size of ini file */
    if (!(hMMFile = CreateFileMapping ((HANDLE)0xffffffff,
				   NULL,
				   PAGE_READWRITE,
				   0,
				   nFileSize * 2,
				   szMapFileName)))
	return FALSE;

    /* map a view of this file for writing */
    lpMMFile = (char *)MapViewOfFile (hMMFile, FILE_MAP_WRITE, 0, 0, 0);

    /* load tokens for APIS section */
    LoadString (hDLL, IDS_PORTAPIS, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_APIS, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* load tokens for MESSAGES section */
    LoadString (hDLL, IDS_PORTMESSAGES, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_MESSAGES, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* load tokens for STRUCTURES section */
    LoadString (hDLL, IDS_PORTSTRUCTURES, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_STRUCTURES, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* load tokens for TYPES section */
    LoadString (hDLL, IDS_PORTTYPES, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_TYPES, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* load tokens for MACROS section */
    LoadString (hDLL, IDS_PORTMACROS, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_MACROS, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* load tokens for CONSTANTS section */
    LoadString (hDLL, IDS_PORTCONSTANTS, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_CONSTANTS, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* load tokens for CUSTOM section */
    LoadString (hDLL, IDS_PORTCUSTOM, szSection, MAX_PATH);
    if (!LoadSection (szIniFilePath, szSection, PT_CUSTOM, &nOffset, lpMMFile))
	{
	/* clean up memory mapped file */
	UnmapViewOfFile (lpMMFile);
	CloseHandle (hMMFile);
	return FALSE;
	}

    /* release WRITE view of memory mapped file */
    UnmapViewOfFile (lpMMFile);

	/* success */
	bInit = TRUE;
    return TRUE;
}



/* release memory mapped file view */
void WINAPI FreePortData ()
	{
	if ( ! bInit )
		return;

    /* release memory mapped file */
    CloseHandle (hMMFile);
    }



/* external function to check a string for porting issues */
BOOL WINAPI CheckString (
    char	*lpszSrc,
    DWORD	dwSearch,
    LPRESULT	lprIssue)
{
    BOOL      bRet = FALSE;
    LPPORT    lpToken;
    char      *lpStr = lpszSrc;
    int       nSrcLen = strlen (lpszSrc);
    int       nTokLen;
    char      *lpMMFile = (char *)MapViewOfFile (hMMFile, FILE_MAP_WRITE, 0, 0, 0);


	if ( ! bInit )
	   return FALSE;

	/* if view of file failed */
    if (!lpMMFile)
	return FALSE;

    /* if ignore token */
    if (dwSearch & PT_IGNORETOKEN)
	/* flag token as ignored */
	IgnoreToken (lpszSrc, (LPPORT)lpMMFile);

    else
	/* loop through all characters in string */
	while ((lpStr-lpszSrc) < nSrcLen)
	    {
	    /* initialize lpToken to beginning of list */
	    lpToken = (LPPORT)lpMMFile;

        /* loop thru all tokens */
	    if (nTokLen = GetFirstToken (&lpToken))
	    do
        {
        /* filter tokens for search criteria */
        if (!(lpToken->dwType & PT_IGNORED) &&
            !(dwSearch & lpToken->dwType) &&
            ((dwSearch & PT_IGNORECASE &&
              !strnicmp ((char *)lpToken+lpToken->nPosToken, lpStr, nTokLen)) ||
             !strncmp ((char *)lpToken+lpToken->nPosToken, lpStr, nTokLen)))
            {
            /* token found in line, return ISSUE struct */
            strncpy (lprIssue->lpszToken,
                 (char *)lpToken+lpToken->nPosToken,
                 *(WORD *)lprIssue->lpszToken);
            strncpy (lprIssue->lpszHelpStr,
                 (char *)lpToken+lpToken->nPosHelpStr,
                 *(WORD *)lprIssue->lpszHelpStr);
            strncpy (lprIssue->lpszIssue,
                 (char *)lpToken+lpToken->nPosIssue,
                 *(WORD *)lprIssue->lpszIssue);
            strncpy (lprIssue->lpszSuggest,
                 (char *)lpToken+lpToken->nPosSuggest,
                 *(WORD *)lprIssue->lpszSuggest);
            lprIssue->nPosToken = (int)(lpStr - lpszSrc);

		    bRet = TRUE;
		    goto DONE;
		    }
		}
	    while ((nTokLen = GetNextToken (&lpToken)));

        lpStr = CharNext(lpStr);
        }

DONE:

    /* unmap view of memory mapped file */
    UnmapViewOfFile (lpMMFile);

    return bRet;
}



/* function get's the first token in the list */
int WINAPI GetFirstToken (
    LPPORT    *lpToken)
{
    /* increment to next non-ignored token in list */
    while (((((LPPORT)*lpToken)->dwType) == PT_IGNORED) &&
	   ((((LPPORT)*lpToken)->nSize) != 0))
	(char *)*lpToken += ((LPPORT)*lpToken)->nSize;

    /* if at end of list, reset list to null */
    if ((((LPPORT)*lpToken)->nSize) == 0)
	{
	*lpToken = 0;
	return 0;
	}

    /* return length of token */
    return (strlen ((char *)*lpToken + ((LPPORT)*lpToken)->nPosToken));
}


/* function get's the next token in the list */
int WINAPI GetNextToken (
    LPPORT    *lpToken)
{
    /* increment to next non-ignored token in list */
    do
	(char *)*lpToken += ((LPPORT)*lpToken)->nSize;
    while (((((LPPORT)*lpToken)->dwType) == PT_IGNORED) &&
	   ((((LPPORT)*lpToken)->nSize) != 0));

    /* if at end of list, reset list to null */
    if ((((LPPORT)*lpToken)->nSize) == 0)
	{
	*lpToken = 0;
	return 0;
	}

    /* return length of token */
    return (strlen ((char *)*lpToken + ((LPPORT)*lpToken)->nPosToken));
}



/* function sets the ignore flag on the specified token */
void WINAPI IgnoreToken (
    char    *lpszToken,
    LPPORT  lpToken)
{
    /* search for token in list */
    while (lpToken->nSize != 0)
	/* if same token */
	if (!strcmp ((char *)((char *)lpToken + lpToken->nPosToken), lpszToken))
	    {
	    lpToken->dwType |= PT_IGNORED;
	    break;
	    }
	/* increment to  next token */
	else
	    (char *)lpToken += lpToken->nSize;
}




/* load tokens from a section of ini file */
BOOL WINAPI LoadSection (
    char    *lpszIniFile,
    char    *lpszSection,
    DWORD   dwType,
    int     *nOffset,
    char    *lpMMFile)
{
    char    *lpszKeyNames;
    char    *lpKey;
    char    *lpszValue;
    char    *lpVal;
    char    *lpszToken;
    char    lpszDefault[] = "Default";
    char    *lpMem = lpMMFile + *nOffset;
    int     nList;


    /* allocate lots of memory off heap to save calling applications' stack */
    if (!(lpszKeyNames = (char *)LocalAlloc (LPTR, FIFTY_K_LINE)))
	return FALSE;
    if (!(lpszValue = (char *)LocalAlloc (LPTR, TWO_K_LINE)))
	{
	LocalFree ((HLOCAL)lpszKeyNames);
	return FALSE;
	}
    if (!(lpszToken = (char *)LocalAlloc (LPTR, MAXTOKENLEN)))
	{
	LocalFree ((HLOCAL)lpszKeyNames);
	LocalFree ((HLOCAL)lpszValue);
	return FALSE;
	}

    /* get all keynames in section */
    if (((nList = GetPrivateProfileString (lpszSection,
					   NULL,
					   lpszDefault,
					   lpszKeyNames,
					   FIFTY_K_LINE,
					   lpszIniFile)) == (int)(strlen (lpszDefault))) &&
	!strcmp (lpszDefault, lpszKeyNames))
	{
	LocalFree ((HLOCAL)lpszKeyNames);
	LocalFree ((HLOCAL)lpszValue);
	LocalFree ((HLOCAL)lpszToken);
	return FALSE;
	}

    /* initialize token pointer and first token */
    lpKey = lpszKeyNames;

    /* loop through all keynames */
    while (TRUE)
	{
	/* get next token */
	strcpy (lpszToken, lpKey);

	/* get value for token */
	if ((GetPrivateProfileString (lpszSection,
				     lpszToken,
				     lpszDefault,
				     lpszValue,
				     TWO_K_LINE,
				     lpszIniFile) == strlen (lpszDefault)) &&
	    !strcmp (lpszDefault, lpszValue))
	    {
	    LocalFree ((HLOCAL)lpszKeyNames);
	    LocalFree ((HLOCAL)lpszValue);
	    LocalFree ((HLOCAL)lpszToken);
	    return FALSE;
	    }
	else
	    {
	    /* break line up into components */
	    ((LPPORT)lpMem)->nPosToken = sizeof (PORT);
	    strcpy ((char *)(lpMem + ((LPPORT)lpMem)->nPosToken), lpszToken);

	    ((LPPORT)lpMem)->nPosHelpStr =
		    ((LPPORT)lpMem)->nPosToken + strlen ((char *)(lpMem + ((LPPORT)lpMem)->nPosToken)) + 1;
	    if (lpVal = strtok (lpszValue, ";"))
		strcpy ((char *)(lpMem + ((LPPORT)lpMem)->nPosHelpStr), lpVal);
	    else
		*(lpMem + ((LPPORT)lpMem)->nPosHelpStr) = 0;

	    ((LPPORT)lpMem)->nPosIssue =
		    ((LPPORT)lpMem)->nPosHelpStr + strlen ((char *)(lpMem + ((LPPORT)lpMem)->nPosHelpStr)) + 1;
	    if (lpVal = strtok (NULL, ";"))
		strcpy ((char *)(lpMem + ((LPPORT)lpMem)->nPosIssue), lpVal);
	    else
		*(lpMem + ((LPPORT)lpMem)->nPosIssue) = 0;

	    ((LPPORT)lpMem)->nPosSuggest =
		    ((LPPORT)lpMem)->nPosIssue + strlen ((char *)(lpMem + ((LPPORT)lpMem)->nPosIssue)) + 1;
	    if (lpVal = strtok (NULL, ";"))
		strcpy ((char *)(lpMem + ((LPPORT)lpMem)->nPosSuggest), lpVal);
	    else
		*(lpMem + ((LPPORT)lpMem)->nPosSuggest) = 0;

	    /* set size of dynamic token structure */
	    ((LPPORT)lpMem)->nSize = (((LPPORT)lpMem)->nPosSuggest +
		 strlen ((char *)(lpMem + ((LPPORT)lpMem)->nPosSuggest)) + 1);

	    /* adjust nSize for DWORD alignment */
            ((LPPORT)lpMem)->nSize = ((((LPPORT)lpMem)->nSize) + 3) & ~3;

	    /* set token type */
	    ((LPPORT)lpMem)->dwType = dwType;

	    /* adjust list pointer to point beginning of next list element */
	    lpMem += ((LPPORT)lpMem)->nSize;
	    }

	/* increment token pointer and test for end of list */
	if (((lpKey += strlen (lpszToken) + 1) - lpszKeyNames) >= (nList - 1))
	    {
	    /* indicate end of list by setting size of next element to 0 */
	    ((LPPORT)lpMem)->nSize = 0;
	    break;
	    }
	}

    /* got to end of section, clean up and go away */
    LocalFree ((HLOCAL)lpszKeyNames);
    LocalFree ((HLOCAL)lpszValue);
    LocalFree ((HLOCAL)lpszToken);

    /* recalculate offset */
    *nOffset = lpMem - lpMMFile;

    /* return successful load */
    return TRUE;
}


/* retrieve ini file and path */
BOOL WINAPI GetIniFile (
	HANDLE	hDLL,
	char	*lpszFileName,
    char    *lpszFile)
{
    char    lpszPath[MAX_PATH];
    char    *lpPath;
	// char	 lpszFileName[MAX_PATH];
	OFSTRUCT	of;

	// Is this a full path ? If so there must be a : or \ in there somewhere.
	lpPath = lpszFileName;
	while ( *lpPath )
	{
	   if ( *lpPath == '\\' || *lpPath == ':' )
	   {
		  if ((OpenFile (lpszFileName, &of, OF_EXIST)))
		  {
			 // Name passed in is OK.
			 strcpy (lpszFile, lpszFileName);
			 return TRUE;
		  }

	   }
	   lpPath = CharNext(lpPath);
	}

	// OK, not a full path, we must build one

    /* get module directory and path */
    GetModuleFileName (hDLL, lpszPath, MAX_PATH);
    lpPath = lpszPath + strlen (lpszPath);

    /* find end of path by searching backwards from end to first '\' or ':' */
    while (lpPath > lpszPath)
    {
    if (*lpPath == '\\' ||
        *lpPath == ':')
        {
        lpPath++;
        break;
        }
    lpPath = CharPrev(lpszPath, lpPath);
    }

    /* terminate at end of path */
    *lpPath = 0;

    /* append ini filename to path */
	strcat (lpPath, lpszFileName);

	// check for an extension ( look for '.' before '\' )
	lpPath = lpszPath+strlen(lpszPath);
	while ( lpPath > lpszPath && 
            *lpPath != '\\' && 
            *lpPath != ':')
	{
	   if ( *lpPath == '.')
		  break;
	   lpPath = CharPrev(lpszPath, lpPath);
	}

	if ( *lpPath != '.' )
	{
	   // No extension, supply one
	   strcat (lpszPath, ".INI");
	}


    /* test for existance */
    if (!(OpenFile (lpszPath, &of, OF_EXIST)))
	{
	GetWindowsDirectory (lpszPath, MAX_PATH);
	strcat (lpszPath, lpszFileName);
	if (!(OpenFile (lpszPath, &of, OF_EXIST)))
	{
		return FALSE;
	}
	else
	    {
	    strcpy (lpszFile, lpszPath);
	    return TRUE;
	    }
	}
    else
	{
	strcpy (lpszFile, lpszPath);
	return TRUE;
	}
}