/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/parse.cpp $
 * $Revision: /main/ms-d/20 $
 *     $Date: 2013-09-30 15:18:02 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/parse.cpp$ 
 * 
 * /main/ms-d/20   2013-09-30 15:18:02   pmackent 
 * Corrected parsing for rounded integer (IPIS100091577)
 *
 * /main/ms-d/19   2013-09-25 15:21:38   pmackent
 * New methods for signal strength display in 3G networks (IPIS100091577)
 *
 * /main/ms-d/18   2013-06-20 14:19:54   pmackent
 * Skip over blanks before USC2 string parameters in some EHS5 firmware versions
 *
 * /main/ms-d/17   2013-04-12 09:21:50   pmackent
 * Adaptions for PXS8 2.910
 *
 * /main/ms-d/16   2013-03-05 11:29:51   pmackent
 * Workaround for unclear UCS2/GSM coding in +STK URC
 *
 * /main/ms-d/15   2013-01-14 10:33:43   pmackent
 * Allow update of multiple NV items
 *
 * /main/ms-d/14   2012-08-24 10:28:40   pmackent
 * Skip over leading blanks in string parameters (workaround for bug in AT+CSCB in PH8)
 *
 * /main/ms-d/13   2012-02-16 12:56:28   ctna0256
 * Separate define for CR-LF problem
 *
 * /main/ms-d/12   2012-02-09 16:44:30   ctna0256
 * Check for missed SMS with AT+CRSM on EHS5, small bug fixes
 *
 * /main/ms-d/11   2012-02-08 15:16:04   ctna0256
 * Add Intel-style SAT
 *
 * /main/ms-d/9   2011-09-26 13:34:48   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/8   2010-04-13 15:55:31   ctna0256
 * Fix problem with fixed point numbers without point
 *
 * /main/ms-d/7   2010-02-03 10:51:02   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/6   2008-08-22 10:24:58   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/5   2008-08-21 09:34:59   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/4   2007-04-10 09:56:44   mp49210
 * Set missing NITZ parameter
 *
 * /main/ms-d/3   2007-01-09 14:36:06   mp49210
 * Change NITZ-URC to UCS2
 *
 * /main/ms-d/2   2006-12-29 13:09:55   mp49210
 * Workaround for upper/lower case problem with AT+CLCK
 *
 * /main/ms-d/1   2006-10-25 16:51:12   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"

#define UCS2_PAGE(x)                ((x) >> 7)
#define UCS2_OFFSET(x)              ((x) & 0x7F)

#define UCS2_EURO                   (0x20AC)
#define UCS2_BLANK                  (0x0020)

#define NO_EXT_GSM_CHAR 0x00
#define NO_VALID_UCS2_CHAR 0x0000

#define GSMESCAPE  0x1b         /* GSM-escape character (03.38) */
#define GSMEURO    0x65         /* eurosymbol in GSM-extension table */

#define GSM_DEFAULT_TABLE 0
#define GSM_EXTENDED_TABLE 1
#define GSM_MAX_NO_OF_TABLES GSM_EXTENDED_TABLE

BOOL EncodedStringSizeToUnicodeStringSize(ENCODING_TYPE enc, size_t cbIn, size_t &cbOut)
{
    BOOL bResult = TRUE;
    switch (enc)
    {
    case ENCODING_GSMDEFAULT:
        // In GSM mode, 1 byte of data maps to 2-bytes (1 char) of unicode
        cbOut = cbIn<<1;
        break;

    case ENCODING_GSMDEFAULT_HEX:
        // In hex mode, 2 bytes of hex data map to 2-bytes (1 char) of unicode
        cbOut = cbIn;
        break;

    case ENCODING_UCS2:
        // In UCS2 mode, 4 bytes of hex data map to 2-bytes (1 char) of unicode
        cbOut = cbIn>>1;
        break;

    case ENCODING_ASCII:
        bResult = FALSE;
        break;

    default:
        bResult = FALSE;
    }

    return bResult;
}

inline void SkipChars(const LPCSTR szData, LPCSTR& rszPointer, LPCSTR szChars)
{
    rszPointer = szData + strspn(szData,szChars);
}

//
// Determine if a string starts with another
//
BOOL MatchStringBeginning(const LPCSTR szString, const LPCSTR szMatch, LPCSTR& rszPointer)
{
    BOOL fRet = FALSE;

    LPCSTR szTemp = strstr(szString, szMatch);
    if (!szTemp)
    {
        goto End;
    }

    fRet = (szTemp == szString);
    if (fRet)
    {
        rszPointer = szString + strlen(szMatch);
    }

    End:
    return fRet;
}

//
// Determine if a string starts with another (or alternative string)
//
BOOL MatchAltStringBeginning(const LPCSTR szString, const LPCSTR szMatch, const LPCSTR szMatchAlt, LPCSTR& rszPointer, BOOL *pWhich)
{
    BOOL fRet = FALSE;
    UINT nLen;
    LPCSTR szTemp = strstr(szString, szMatch);

    nLen = strlen(szMatch);
    if (!szTemp)
    {
        szTemp = strstr(szString, szMatchAlt);
        nLen = strlen(szMatchAlt);
        if (!szTemp)
        {
            goto End;
        }
        else {
            if (pWhich != NULL) *pWhich = TRUE;
        }
    }
    else {
        if (pWhich != NULL) *pWhich = FALSE;
    }

    fRet = (szTemp == szString);
    if (fRet)
    {
        rszPointer = szString + nLen;
    }

    End:
    return fRet;
}


//
// Determine if a string matches the end of another
//
BOOL MatchStringEnd(const LPCSTR szString, const LPCSTR szMatch, LPCSTR& rszPointer)
{
    UINT cbString = strlen(szString);
    UINT cbMatch = strlen(szMatch);
    BOOL fRet = FALSE;

    if (cbString >= cbMatch)
    {
        fRet = !strcmp(szString + cbString - cbMatch, szMatch);
    }
    if (fRet)
    {
        rszPointer = szString + cbString;
    }
    return fRet;
}



//
// Determine if a string contains another
//
BOOL MatchStringAnywhere(const LPCSTR szString, const LPCSTR szMatch, LPCSTR& rszPointer)
{
    BOOL fRet = FALSE;

    LPCSTR szTemp = strstr(szString, szMatch);
    if (!szTemp)
    {
        goto End;
    }

    rszPointer = szTemp + strlen(szMatch);
    fRet = TRUE;

    End:
    return fRet;
}

//
//
//
BOOL ParseUInt(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT& rnInt, LPCSTR& rszPointer)
{
    int nLeadingDigit = -1;
    BOOL Found=FALSE;

    // Skip any spaces
    SkipChars(szData, rszPointer, " ");

    rnInt = 0;

    while (*rszPointer >= '0' && *rszPointer <= '9')
    {
        if (!fLeadingZerosAllowed)
        {
            // Test for leading 0s
            if (-1 == nLeadingDigit)
            {
                // Leading digit hasn't been set yet -- set it now
                nLeadingDigit = *rszPointer - '0';
            }
            else if (!nLeadingDigit)
            {
                // Leading digit is 0 and we got another digit
                // This means that we have leading 0s -- reset the pointer and punt
                Found=FALSE;
                goto Error;
            }
        }

        rnInt = rnInt * 10 + (*rszPointer - '0');
        rszPointer++;
        Found=TRUE;
    }

    // Skip past any spaces
    SkipChars(rszPointer, rszPointer, " ");

    Error:
    if (!Found)
    {
        rszPointer = szData;
    }
    return Found;
}

//
// Like ParseUInt(), but handles the case, that a value might be hexadecimal
// (starting with "0x") or decimal.
//
BOOL ParseUIntHex(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT& rnInt, LPCSTR& rszPointer)
{
    int nLeadingDigit = -1;
    BOOL Found=FALSE;
    BOOL bIsHex = FALSE;

    // Skip any spaces
    SkipChars(szData, rszPointer, " ");

    rnInt = 0;

    // is the number starts with "0x" it is a hexadezimal number.
    if (*rszPointer == '0') {
        if(*(rszPointer+1) == 'x') {
            rszPointer += 2;
            bIsHex = TRUE;
        }
    }

    if (bIsHex) {
        UINT nDigit = 0;

        while ((*rszPointer >= '0' && *rszPointer <= '9') ||
               (*rszPointer >= 'A' && *rszPointer <= 'F') ||
               (*rszPointer >= 'a' && *rszPointer <= 'f'))
        {
            if (!fLeadingZerosAllowed)
            {
                // Test for leading 0s
                if (-1 == nLeadingDigit)
                {
                    // Leading digit hasn't been set yet -- set it now
                    nLeadingDigit = *rszPointer - '0';
                }
                else if (!nLeadingDigit)
                {
                    // Leading digit is 0 and we got another digit
                    // This means that we have leading 0s -- reset the pointer and punt
                    rszPointer = szData;
                    goto Error;
                }
            }

            // Calculate the digit value
            if (*rszPointer >= 'A' && *rszPointer <= 'F')
            {
                nDigit = 10 + *rszPointer - 'A';
            }
            else if (*rszPointer >= 'a' && *rszPointer <= 'f')
            {
                nDigit = 10 + *rszPointer - 'a';
            }
            else
            {
                nDigit = *rszPointer - '0';
            }

            rnInt = rnInt * 16 + nDigit;
            rszPointer++;
            Found=TRUE;
        }
    }
    else {
        while (*rszPointer >= '0' && *rszPointer <= '9')
        {
            if (!fLeadingZerosAllowed)
            {
                // Test for leading 0s
                if (-1 == nLeadingDigit)
                {
                    // Leading digit hasn't been set yet -- set it now
                    nLeadingDigit = *rszPointer - '0';
                }
                else if (!nLeadingDigit)
                {
                    // Leading digit is 0 and we got another digit
                    // This means that we have leading 0s -- reset the pointer and punt
                    Found=FALSE;
                    goto Error;
                }
            }

            rnInt = rnInt * 10 + (*rszPointer - '0');
            rszPointer++;
            Found=TRUE;
        }
    }

    // Skip past any spaces
    SkipChars(rszPointer, rszPointer, " ");

    Error:
    if (!Found)
    {
        rszPointer = szData;
    }
    return Found;
}


//
//
//
BOOL ParseIntAsUInt(const LPCSTR szData, UINT& rnInit, LPCSTR& rszPointer, BOOL bSkipPlusSign)
{
    BOOL fNegative = FALSE;

    rszPointer = szData;

    if (*rszPointer == '-')
    {
        fNegative = TRUE;
        ++rszPointer;
    }
    else if (*rszPointer == '+' && bSkipPlusSign)
    {
        ++rszPointer;
    }

    if (!ParseUInt(rszPointer, bSkipPlusSign, rnInit, rszPointer))   // Numbers with leading plus sign also have leading zeros
        return FALSE;

    if (fNegative)
    {
        rnInit = ~rnInit + 1;
    }

    return TRUE;
}

//
//
//
BOOL ParseUIntAndVerifyAbove(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT nUpperBound, UINT& rnInt, LPCSTR& rszPointer)
{
    BOOL fRet;

    // Parse the number
    fRet = ParseUInt(szData, fLeadingZerosAllowed, rnInt, rszPointer);

    // Verify that the number is greater than the upper bound
    if (fRet && rnInt >= nUpperBound)
    {
        fRet = FALSE;
        rszPointer = szData;
    }
    return fRet;
}


//
//
//
BOOL ParseHexUInt(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT& rnInt, LPCSTR& rszPointer)
{
    int nLeadingDigit = -1;
    UINT nDigit;

    rszPointer = szData;
    rnInt = 0;

    while ((*rszPointer >= '0' && *rszPointer <= '9') ||
           (*rszPointer >= 'A' && *rszPointer <= 'F') ||
           (*rszPointer >= 'a' && *rszPointer <= 'f'))
    {
        if (!fLeadingZerosAllowed)
        {
            // Test for leading 0s
            if (-1 == nLeadingDigit)
            {
                // Leading digit hasn't been set yet -- set it now
                nLeadingDigit = *rszPointer - '0';
            }
            else if (!nLeadingDigit)
            {
                // Leading digit is 0 and we got another digit
                // This means that we have leading 0s -- reset the pointer and punt
                rszPointer = szData;
                goto Error;
            }
        }

        // Calculate the digit value
        if (*rszPointer >= 'A' && *rszPointer <= 'F')
        {
            nDigit = 10 + *rszPointer - 'A';
        }
        else if (*rszPointer >= 'a' && *rszPointer <= 'f')
        {
            nDigit = 10 + *rszPointer - 'a';
        }
        else
        {
            nDigit = *rszPointer - '0';
        }

        rnInt = rnInt * 16 + nDigit;
        rszPointer++;
    }

    Error:
    return (rszPointer > szData);
}

BOOL ParseFixedPoint(const LPCSTR szData, DWORD &rdwFixedPoint, LPCSTR& rszPointer)
{
    UINT dwIntPart;
    UINT dwFracPart;
    LPCSTR szFracStart;
    rszPointer = szData;

    // Parse the integer part
    if (!ParseUInt(rszPointer, TRUE, dwIntPart, rszPointer))
    {
        goto Error;
    }

    // Shift up by 16 bits
    dwIntPart <<= 16;
    rdwFixedPoint = dwIntPart;

    // Parse decimal separator ("." -- this is what GSM uses)
    if ('.' != *rszPointer)
    {
        goto Error;
    }

    // Advance pointer past decimal point
    rszPointer++;

    // Parse the fractional part
    szFracStart=rszPointer;
    if (!ParseUInt(rszPointer, TRUE, dwFracPart, rszPointer))
    {
        goto Error;
    }

    // Shift up by 16 bits
    dwFracPart <<= 16;

    // Now normalize to lie in the lowest 16 bits.
    UINT dwDivisor;
    dwDivisor = 1;
    while (szFracStart<rszPointer)
    {
        dwDivisor*=10;
        szFracStart++;
    }
    dwFracPart/=dwDivisor;

    rdwFixedPoint = dwIntPart+dwFracPart;

    Error:
    return (rszPointer > szData);
}


//
//
//
BOOL ParseIntRounded(const LPCSTR szData, INT& rnInit, LPCSTR& rszPointer)
{
    BOOL fNegative = FALSE;
    UINT uiTmp, uiTmpFraction = 0;

    rszPointer = szData;

    if (*rszPointer == '-')
    {
        fNegative = TRUE;
        ++rszPointer;
    }
    else if (*rszPointer == '+')
    {
        ++rszPointer;
    }

    if (!ParseUInt(rszPointer, TRUE, uiTmp, rszPointer))   // Numbers with leading plus sign also have leading zeros
        return FALSE;

    // There might be a decimal point. Parse the fracition behind
    // in order to do the rounding.
    if (*rszPointer == '.')
    {
        UINT uiDummy;

        fNegative = TRUE;
        ++rszPointer;
        if (*rszPointer < '0' || *rszPointer > '9')
            uiTmpFraction = *rszPointer - '0';
        // Skip rest of value.
        (void)ParseUInt(rszPointer, TRUE, uiDummy, rszPointer);
    }

    rnInit = (INT)uiTmp;
    if (uiTmpFraction >= 5)
        rnInit += 1;
    if (fNegative)
        rnInit *= -1;

    return TRUE;
}

//
//
//
BOOL ParseString(const LPCSTR szData, const LPSTR szOut, const UINT cbOut, LPCSTR& rszPointer)
{
    LPSTR pchOut;
    LPSTR szOutEnd;
    BOOL fRet = FALSE;

    rszPointer = szData;
    pchOut = szOut;
    szOutEnd = szOut + cbOut;

    // Skip over leading blanks.
    while (*rszPointer == ' ') rszPointer++;

    // Skip over the leading quote
    if (*rszPointer != '\"')
    {
        goto Error;
    }
    rszPointer++;

    while (pchOut < szOutEnd - 1)
    {
        if (!*rszPointer)
        {
            // We hit the end of the string -- fail
            goto Error;
        }
        else if ('\"' == *rszPointer)
        {
            // Terminating quote -- skip over it and bail out
            rszPointer++;
            fRet = TRUE;
            break;
        }
        else
        {
            *pchOut++ = *rszPointer++;
        }
    }
    *pchOut = '\0';

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
//
//
BOOL ParseUCS2String(const LPCSTR szData, const LPWSTR pwszOut, const UINT cbOut, LPCSTR& rszPointer)
{
    BOOL fRet = FALSE;

    SkipChars(szData, rszPointer, " ");

    if(!ParseQuotedEncodedString(ENCODING_UCS2, rszPointer, pwszOut, pwszOut+cbOut))
    {
      goto Error;
    }

    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
//
//
BOOL ParseGSMString(const LPCSTR szData, const LPWSTR pwszOut, const UINT cbOut, LPCSTR& rszPointer)
{
    char szTmp[MAX_ATCMD_LEN];
    BOOL fRet = FALSE;
    INT iLen, iIdx, iIdxOut;
    char cTmp;

    rszPointer = szData;

    if (!ParseString(rszPointer, szTmp, MAX_ATCMD_LEN, rszPointer))
    {
      goto Error;
    }

    iLen = strlen(szTmp);
    iIdx = 0;
    iIdxOut = 0;
    while (iIdx < iLen && iIdxOut < (INT)cbOut) {
        cTmp = szTmp[iIdx];
        if (cTmp == '\\' && iIdx < iLen-2) {
            // Special charcters are displeyd as an escape sequence "\XX".
            // For example "\00" means "@" (GSM coded).
            cTmp = SemiByteCharsToByte(szTmp[iIdx+1], szTmp[iIdx+2]);
            iIdx += 2;
        }
        (void)GSMCharToUnicode(cTmp, &pwszOut[iIdxOut]);
        iIdx++;
        iIdxOut++;
    }
    pwszOut[iIdxOut] = 0;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

#define CAST_CTOW(c) ((WCHAR)((BYTE)(c)))

#pragma warning( push )
// Warning disabled to keep code align with reference RIL.
#pragma warning(disable : 4244)
static BOOL ParseStringGSMW(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    // Handle empty string
    if (pszIn==pszInEnd)
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    BYTE c;
    c = *pszIn++;
    switch (c)
    {
    case 0x80:
        {
            // String data is Unicode bytes in UCS2 encoding
            BOOL fOdd = TRUE;
            while (pwszOut < pwszOutEnd)
            {
                if (pszIn==pszInEnd)
                {
                    // We don't want to fail if the string ends in a partial character.  Just
                    // truncate it.  This will overwrite a trailing 1/2 char with the NULL byte
                    *pwszOut = L'\0';
                    return TRUE;
                }

                c = *pszIn++;

                // Assemble the UCS2 character incrementally
                if (fOdd)
                {
                    *pwszOut = CAST_CTOW(c) << 8;
                }
                else
                {
                    *pwszOut++ |= CAST_CTOW(c);
                }
                fOdd = !fOdd;

            }
            return FALSE;
        }
    case 0x81:
    case 0x82:
        {
            // Make sure at least 3 bytes of data
            if ( (pszIn+3) > pszInEnd )
            {
                return FALSE;
            }

            WCHAR UCSBase;
            if (c==0x81)
            {
                // Third octet is bits 14-7 of the base pointer
                UCSBase = CAST_CTOW(*pszIn++) << 7;
            }
            else
            {
                // Third & fourth octets are the base pointer
                UCSBase =  CAST_CTOW(*pszIn++) << 8;
                UCSBase += CAST_CTOW(*pszIn++);
            }

            if (pszIn>=pszInEnd)
            {
                return FALSE;
            }

            while (pwszOut < pwszOutEnd)
            {
                if (pszIn==pszInEnd)
                {
                    *pwszOut = L'\0';
                    return TRUE;
                }

                c = *pszIn++;

                if (c & 0x80)
                {
                    // If high bit is set, lower 7 bits constitute the lowest 7 bits added to the base pointer
                    *pwszOut = UCSBase + CAST_CTOW(c & 0x7f);
                }
                else
                {
                    // This is a GSM default alphabet character
                    *pwszOut = CAST_CTOW(c);  // Avoid sign-extension
                }

                pwszOut++;
            }
            return FALSE;
        }
    default:
        {
            // Normal string data - similar to ParseString above
            while (pwszOut < pwszOutEnd)
            {
                (void)GSMCharToUnicode(c, pwszOut);
                pwszOut++;

                if (pszIn==pszInEnd)
                {
                    *pwszOut = L'\0';
                    return TRUE;
                }

                c = *pszIn++;
            }
        }
    }

    return FALSE;
}
#pragma warning( pop )

static BOOL ParseStringHexW(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    BOOL bRet;

    // Handle empty string (must have at least two characters).
    if ( (pszIn+2) > pszInEnd )
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    UINT cbInTmp = ((pszInEnd-pszIn)>>1);

    LPSTR pszInTmp = (LPSTR)LocalAlloc(0,cbInTmp);
    if (!pszInTmp)
    {
        return FALSE;
    }

    bRet = GSMHexToGSM(pszIn, pszInEnd-pszIn, pszInTmp, cbInTmp, cbInTmp);
    if (!bRet)
    {
        goto Exit;
    }

    bRet = ParseStringGSMW(pszInTmp, pszInTmp+cbInTmp, pwszOut, pwszOutEnd);

Exit:
    LocalFree(pszInTmp);
    return bRet;
}

static BOOL ParseStringUCS2(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    BOOL bRet;
    UINT cbCount;
    LPSTR pszInBuf;

    // Handle empty string (must have at least two characters).
    if ((pszIn+2) > pszInEnd)
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    UINT cbInTmp = ((pszInEnd - pszIn) >> 1);

    LPSTR pszInTmp = (LPSTR)LocalAlloc(0, cbInTmp);
    pszInBuf = pszInTmp;
    if (!pszInTmp)
    {
        return FALSE;
    }

    bRet = GSMHexToGSM(pszIn, pszInEnd-pszIn, pszInTmp, cbInTmp, cbInTmp);
    if (!bRet)
    {
        goto Exit;
    }

    for (cbCount = 0; cbCount < cbInTmp; cbCount += 2)
    {
        if (pwszOut >= pwszOutEnd) break;
        *pwszOut   = (((BYTE)(pszInTmp[cbCount])) << 8);
        *pwszOut  |= ((BYTE)(pszInTmp[cbCount + 1]));
        pwszOut++;
    }
    *pwszOut = L'\0';

    bRet = TRUE;

Exit:
    LocalFree(pszInBuf);
    return bRet;
}

BOOL ParseEncodedString(ENCODING_TYPE enc, LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    switch (enc)
    {
    case ENCODING_GSMDEFAULT:
        return ParseStringGSMW(pszIn, pszInEnd, pwszOut, pwszOutEnd);

    case ENCODING_GSMDEFAULT_HEX:
        return ParseStringHexW(pszIn, pszInEnd, pwszOut, pwszOutEnd);

    case ENCODING_UCS2:
        return ParseStringUCS2(pszIn, pszInEnd, pwszOut, pwszOutEnd);
    }

    return FALSE;
}

BOOL ParseQuotedEncodedString(ENCODING_TYPE enc, LPCSTR& pszIn, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    LPCSTR pszInTmp = pszIn;

    // Skip over the leading quote
    DWORD c;
    c = *pszInTmp++;
    if (c != '\"')
    {
        return FALSE;
    }

    // Calculate string length
    LPCSTR pszInEnd = strchr(pszInTmp, '\"');
    if (!pszInEnd)
    {
        return FALSE;
    }

    if (!ParseEncodedString(enc, pszInTmp, pszInEnd, pwszOut, pwszOutEnd))
    {
        return FALSE;
    }

    pszIn = pszInEnd+1;

    return TRUE;
}

#pragma warning( push )
// Warning disabled to keep code align with reference RIL.
#pragma warning(disable : 4706)
static BOOL AppendStringUCS2W(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd)
{
    LPSTR pszOutTmp = pszOut;
    LPCWSTR pwszInTmp = pwszIn;
    WCHAR wc;

    if (pszOutTmp>=pszOutEnd)
    {
        return FALSE;
    }
    *pszOutTmp++ = (BYTE)0x80;

    // Iterate through all the characters in the buffer and store
    pwszInTmp = pwszIn;
    while (wc=*pwszInTmp++)
    {
        if (pszOutTmp>=pszOutEnd)
        {
            return FALSE;
        }
        *pszOutTmp++=(BYTE)(wc>>8);

        if (pszOutTmp>=pszOutEnd)
        {
            return FALSE;
        }
        *pszOutTmp++=(BYTE)(wc);
    }

    pszOut = pszOutTmp;
    return TRUE;
}

static BOOL AppendStringGSMW(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode, BOOL bFilterGsmChars)
{
    LPSTR pszOutTmp = pszOut;
    LPCWSTR pwszInTmp = pwszIn;
    WCHAR wc;

    while (wc=*pwszInTmp++)
    {
        // If character can't be converted to GSM,
        // or if it's a control character (which can screw up the module)
        // or if it's a double-quote character (which will also screw up the module)
        // Then skip it (or switch to Unicode if allowed)
        char c;
        if ( (!UnicodeCharToGSM(wc, &c)) ||
             (bFilterGsmChars && ((c<32) || (c=='\"'))) )
        {
            if (bAllowUnicode)
            {
                // Append in unicode mode
                return AppendStringUCS2W(pwszIn,pszOut,pszOutEnd);
            }
            else
            {
                // Skip the character
                continue;
            }
        }

        if (pszOutTmp>=pszOutEnd)
        {
            return FALSE;
        }
        *pszOutTmp++=c;
    }

    pszOut = pszOutTmp;
    return TRUE;
}
#pragma warning( pop )

static BOOL AppendStringHexW(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    BOOL bRet;
    UINT cbIn, cbOut;

    // Allocate a buffer to hold the raw (non-hex) gsm characters
    cbIn = _tcslen(pwszIn);

    // If we might use unicode, we need enough space in the intermediate buffer to hold the Unicode format,
    // which takes 2 bytes per unicode char, plus 1 additional byte at the beginning.
    if (bAllowUnicode)
    {
        cbIn = (cbIn<<1) + 1;
    }

    // Handle empty string.
    if (cbIn==0)
    {
        return TRUE;
    }

    LPSTR pszIn = (LPSTR)LocalAlloc(0,cbIn);
    if (!pszIn)
    {
        return FALSE;
    }

    // Pass in a temp ptr, since this will update the ptr and we don't want to lose pszIn.
    LPSTR pszInTmp=pszIn;
    bRet = AppendStringGSMW(pwszIn, pszInTmp, pszInTmp + cbIn, bAllowUnicode, FALSE);
    if (!bRet)
    {
        goto Exit;
    }

    cbIn = pszInTmp-pszIn;
    cbOut = pszOutEnd-pszOut;
    bRet = GSMToGSMHex(pszIn, cbIn, pszOut, cbOut, cbOut);
    pszOut = pszOut + cbOut;

Exit:
    LocalFree(pszIn);
    return bRet;
}

static BOOL AppendEncodedString(ENCODING_TYPE enc, LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    BOOL fResult = FALSE;

    switch (enc)
    {
    case ENCODING_GSMDEFAULT:
        fResult = AppendStringGSMW(pwszIn,pszOut,pszOutEnd,bAllowUnicode,TRUE);
        break;
    case ENCODING_GSMDEFAULT_HEX:
        fResult = AppendStringHexW(pwszIn,pszOut,pszOutEnd,bAllowUnicode);
        break;
    default:
        fResult = FALSE;
    }

    return fResult;
}

BOOL AppendQuotedEncodedString(ENCODING_TYPE enc, LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    BOOL bResult;

    LPSTR pszOutTmp = pszOut;
    if (pszOutTmp>=pszOutEnd)
    {
        return FALSE;
    }

    *pszOutTmp++='\"';
    if (pszOut>=pszOutEnd)
    {
        return FALSE;
    }

    bResult = AppendEncodedString(enc,pwszIn,pszOutTmp,pszOutEnd,bAllowUnicode);
    if (!bResult)
    {
        return FALSE;
    }

    *pszOutTmp++='\"';
    if (pszOutTmp>=pszOutEnd)
    {
        return FALSE;
    }

    *pszOutTmp='\0'; // Put on a NULL just to be safe
    pszOut = pszOutTmp;
    return TRUE;
}

//
//
//
BOOL ParseUnquotedString(const LPCSTR szData, const char chDelimiter, const LPSTR szOut, const UINT cbOut, LPCSTR& rszPointer)
{
    LPSTR pchOut;
    LPSTR szOutEnd;
    BOOL fRet = FALSE;

    rszPointer = szData;
    pchOut = szOut;
    szOutEnd = szOut + cbOut;

    // Copy the string data until we encounter the delimiter character
    while (chDelimiter != *rszPointer && pchOut < szOutEnd - 1)
    {
        *pchOut++ = *rszPointer++;
    }
    *pchOut = '\0';

    // Skip forward until the delimiter character, if we haven't reached it yet
    if (chDelimiter != *rszPointer)
    {
        rszPointer = strchr(rszPointer, chDelimiter);
        if (!rszPointer)
        {
            // We didn't find the delimiter in the string -- fail
            goto Error;
        }
    }
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
// NOTE: caller is responsible for free'ing the storage used by rszOut
//
BOOL ParseUnlimitedString(const LPCSTR szData, LPSTR& rszOut, UINT& rcbOut, LPCSTR& rszPointer)
{
    LPCSTR pchTerminatingQuote;
    BOOL fRet = FALSE;

    rszOut = NULL;
    rszPointer = szData;

    // Skip over the leading quote
    if (*rszPointer != '\"')
    {
        goto Error;
    }
    rszPointer++;

    // Calculate string length
    pchTerminatingQuote = strchr(rszPointer, '\"');
    if (!pchTerminatingQuote)
    {
        goto Error;
    }
    rcbOut = (pchTerminatingQuote - rszPointer) + 1;

    // Allocate sufficient storage for the string
    rszOut = new char[rcbOut];
    if (!rszOut)
    {
        goto Error;
    }

    // Copy the string into the allocated buffer
    (void)strncpyz(rszOut, rszPointer, rcbOut);
    *(rszOut + rcbOut - 1) = '\0';
    rszPointer = pchTerminatingQuote + 1;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete[] rszOut;
        rszOut = NULL;
        rcbOut = 0;
        rszPointer = szData;
    }
    return fRet;
}

//
// NOTE: caller is responsible for free'ing the storage used by rszOut
//
BOOL ParseUnlimitedUnquotedString(const LPCSTR szData, const char chDelimiter, LPSTR& rszOut, UINT& rcbOut, LPCSTR& rszPointer)
{
    LPCSTR pchTerminator;
    BOOL fRet = FALSE;

    rszOut = NULL;
    rszPointer = szData;

    // Calculate string length
    pchTerminator = strchr(rszPointer, chDelimiter);
    if (!pchTerminator)
    {
        goto Error;
    }
    rcbOut = (pchTerminator - rszPointer) + 1;

    // Allocate sufficient storage for the string
    rszOut = new char[rcbOut];
    if (!rszOut)
    {
        goto Error;
    }

    // Copy the string into the allocated buffer
    (void)strncpyz(rszOut, rszPointer, rcbOut);
    *(rszOut + rcbOut - 1) = '\0';
    rszPointer = pchTerminator;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete[] rszOut;
        rszOut = NULL;
        rcbOut = 0;
        rszPointer = szData;
    }
    return fRet;
}


//
//
//
BOOL ParseRange(const LPCSTR szData, RILRANGE& rrrRange, LPCSTR& rszPointer)
{
    UINT nValue;
    BOOL fRet = FALSE;

    rszPointer = szData;

    // Parse "("
    if ('(' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    // Parse "<min>"
    if (!ParseUInt(rszPointer, TRUE, nValue, rszPointer))
    {
        goto Error;
    }
    rrrRange.dwMinValue = nValue;

    // Try parsing ")" for ranges with a single value
    if (')' == *rszPointer)
    {
        rrrRange.dwMaxValue = nValue;
        rszPointer++;
        fRet = TRUE;
        goto Error; // ...although not really an error
    }

    // Parse "-"
    if ('-' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    // Parse "<max>"
    if (!ParseUInt(rszPointer, TRUE, nValue, rszPointer))
    {
        goto Error;
    }
    rrrRange.dwMaxValue = nValue;

    // Parse ")"
    if (')' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
//
//
BOOL ParseRangesFromString(const LPCSTR szData,
                           RILRANGE rgrrRange[],
                           DWORD& rdwNumRanges,
                           DWORD dwMaxRanges,
                           LPCSTR& rszPointer)
{
    UINT nValue1;
    UINT nValue2;
    DWORD dwCurrentRange = 0;
    BOOL fRet = FALSE;

    rszPointer = szData;

    while (dwCurrentRange < dwMaxRanges)
    {
        // Parse the first value
        if (!ParseUInt(rszPointer, TRUE, nValue1, rszPointer))
        {
            goto Error;
        }

        // Parse "-"
        if (MatchStringBeginning(rszPointer, "-", rszPointer))
        {
            // Parse the second value
            if (!ParseUInt(rszPointer, TRUE, nValue2, rszPointer))
            {
                goto Error;
            }
        }
        else if (MatchStringBeginning(rszPointer, "x", rszPointer))
        {
            // The second digit of Value1 can be anything
            nValue1 *= 10;
            nValue2 = nValue1 + 9;
        }
        else
        {
            nValue2 = nValue1;
        }

        rgrrRange[dwCurrentRange].dwMinValue = nValue1;
        rgrrRange[dwCurrentRange].dwMaxValue = nValue2;

        dwCurrentRange++;

        // Parse ","
        if (',' != *rszPointer)
        {
            break;
        }
        rszPointer++;
    }

    rdwNumRanges = dwCurrentRange;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareListValues(const void* pElem1, const void* pElem2)
{
    UINT n1 = ((LISTVALUEMAP*)pElem1)->nValue;
    UINT n2 = ((LISTVALUEMAP*)pElem2)->nValue;
    int iRet;

    if (n1 < n2)
    {
        iRet = -1;
    }
    else if (n1 == n2)
    {
        iRet = 0;
    }
    else
    {
        iRet = 1;
    }
    return iRet;
}

BOOL FlagFromValue(UINT nValue, const LISTVALUEMAP* const rglsmMapArray, const UINT nMapArrayLength, DWORD& Flag)
{
    LISTVALUEMAP lsmKey;
    LISTVALUEMAP* plsmFound;

    // Add correspondig flag to the mask
    lsmKey.nValue = nValue;
    plsmFound = (LISTVALUEMAP*)bsearch(&lsmKey, rglsmMapArray, nMapArrayLength, sizeof(LISTVALUEMAP), BSCompareListValues);
    if (plsmFound)
    {
        Flag = plsmFound->dwFlag;
        return TRUE;
    }
    else
    {
        Flag=0;
        return FALSE;
    }
}

//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareListStrings(const void* pElem1, const void* pElem2)
{
    LPCSTR sz1 = ((LISTSTRINGMAP*)pElem1)->szString;
    LPCSTR sz2 = ((LISTSTRINGMAP*)pElem2)->szString;
    return strcmp(sz1, sz2);
}

BOOL FlagFromString(const LPCSTR szString, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength, DWORD& Flag)
{
    LISTSTRINGMAP lsmKey;
    LISTSTRINGMAP* plsmFound;

    // Add correspondig flag to the mask
    lsmKey.szString = szString;
    plsmFound = (LISTSTRINGMAP*)bsearch(&lsmKey, rglsmMapArray, nMapArrayLength, sizeof(LISTSTRINGMAP), BSCompareListStrings);
    if (plsmFound)
    {
        Flag = plsmFound->dwFlag;
        return TRUE;
    }
    else
    {
        Flag=0;
        return FALSE;
    }
}

BOOL ValueFromFlag(UINT dwFlag, const LISTVALUEMAP* const rglsmMapArray, const UINT nMapArrayLength, UINT& Value)
{
    UINT i;
    for (i=0;i<nMapArrayLength;i++)
    {
        if (rglsmMapArray[i].dwFlag == dwFlag)
        {
            Value=rglsmMapArray[i].nValue;
            return TRUE;
        }
    }
    return FALSE;
}

BOOL StringFromFlag(UINT dwFlag, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength, LPCSTR& szString)
{
    UINT i;
    for (i=0;i<nMapArrayLength;i++)
    {
        if (rglsmMapArray[i].dwFlag == dwFlag)
        {
            szString=rglsmMapArray[i].szString;
            return TRUE;
        }
    }
    return FALSE;
}

//
//
//
BOOL ParseRangeList(const LPCSTR szData, const LISTVALUEMAP* const rglvmMapArray, const UINT nMapArrayLength, DWORD& rdwMask,
                    LPCSTR& rszPointer)
{
    UINT nValue1;
    UINT nValue2;
    UINT i;
    BOOL fRet = FALSE;

    rdwMask = 0x00000000;
    rszPointer = szData;

    // Parse "("
    if ('(' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    for (;;)
    {
        // Parse the first value
        if (!ParseUInt(rszPointer, TRUE, nValue1, rszPointer))
        {
            goto Error;
        }

        // Parse "-"
        if (MatchStringBeginning(rszPointer, "-", rszPointer))
        {
            // Parse the second value
            if (!ParseUInt(rszPointer, TRUE, nValue2, rszPointer))
            {
                goto Error;
            }
        }
        else if (MatchStringBeginning(rszPointer, "x", rszPointer))
        {
            // The second digit of Value1 can be anything
            nValue1 *= 10;
            nValue2 = nValue1 + 9;
        }
        else if (MatchStringBeginning(rszPointer, "X", rszPointer))
        {
            // The second digit of Value1 can be anything
            nValue1 *= 10;
            nValue2 = nValue1 + 9;
        }
        else
        {
            nValue2 = nValue1;
        }

        // Walk through this sub-range and add corresponding flags to the mask
        for (i = nValue1; i <= nValue2; i++)
        {
            DWORD Flag;
            if (FlagFromValue(i,rglvmMapArray,nMapArrayLength,Flag))
            {
                rdwMask |= Flag;
            }
        }

        // Parse ","
        if (',' != *rszPointer)
        {
            break;
        }
        rszPointer++;
    }

    // Parse ")"
    if (')' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
//
//
BOOL ParseStringList(const LPCSTR szData, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength, DWORD& rdwMask,
                     LPCSTR& rszPointer)
{
    char szString[MAX_ATCMD_LEN];
    BOOL fRet = FALSE;

    rdwMask = 0x00000000;
    rszPointer = szData;

    // Parse "("
    if ('(' != *rszPointer)
    {
        // This isn't a complete SIM Toolkit notification -- no need to parse it
        goto Error;
    }
    rszPointer++;

    for (;;)
    {
        // Parse the next string
        if (!ParseString(rszPointer, szString, MAX_ATCMD_LEN, rszPointer))
        {
            goto Error;
        }

        // Add corresponding flag to the mask
        DWORD Flag;
        if (FlagFromString(szString, rglsmMapArray, nMapArrayLength,Flag))
        {
            rdwMask |= Flag;
        }

        // Parse ","
        if (',' != *rszPointer)
        {
            break;
        }
        rszPointer++;
    }

    // Parse ")"
    if (')' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}


// Note that if ParsePartialResponse thinks there is a partial response, it will throw away
// the partial response up to the '+' character of the next response. This means that it will
// also throw away any leading <cr><lf> prefix on the next call. This works because
// ParseRspPrefix always returns TRUE, even if the <cr><lf> prefix isn't there. So, if you
// ever have ParseRspPrefix return FALSE if the prefix isn't there, go back into ParsePartialResponse
// and fix it to leave the prefix at the beginning of the next response.
//
//
//
BOOL ParseRspPrefix(const LPCSTR szData, LPCSTR& rszPointer)
{
    if (!szData) return FALSE;

    UINT len = strlen(szData);

    for (UINT i = 0; i < len; i++) {
        if ((szData[i] != '\r') && (szData[i] != '\n')) break;
    }
    rszPointer += i;

    return TRUE;
}


//
//
//
BOOL ParseRspPostfix(const LPCSTR szData, LPCSTR& rszPointer)
{
    BOOL fRet;

    fRet = MatchStringBeginning(szData, "\r\n", rszPointer);

    if (!fRet && CR_LF_MISSMATCH) {
        // Problem with EHS5 Rel1: sometimes <CR><<LF> is replaced by <LF><CR><LF>
        fRet = MatchStringBeginning(szData, "\n\r\n", rszPointer);
    }

    return fRet;
}


//
//
//
BOOL ParseRspParamSeparator(const LPCSTR szData, LPCSTR& rszPointer)
{
    return MatchStringBeginning(szData, ",", rszPointer);
}


//
//
//
BOOL FindRspPostfix(const LPCSTR szData, LPCSTR& rszPointer)
{
    BOOL fRet;

    fRet = MatchStringAnywhere(szData, "\r\n", rszPointer);

    return fRet;
}

//
//
//
#ifdef RIL_NOTIFY_NITZ
BOOL ParseSystemTimeNITZ(LPSYSTEMTIME pTime, const WCHAR *wszTime)
{
    BOOL fRet = FALSE;

    // Format: YY/MM/DD,HH:MM:SS
    if (wcslen(wszTime) == 17 && wszTime[2]=='/' && wszTime[5]=='/' && wszTime[8]==',' && wszTime[11]==':' && wszTime[14]==':')
    {
        memset(pTime, 0, sizeof(SYSTEMTIME));
        pTime->wYear = 2000+(WORD)_wtoi(wszTime);
        pTime->wMonth = (WORD)_wtoi(wszTime+3);
        pTime->wDay = (WORD)_wtoi(wszTime+6);
        pTime->wHour = (WORD)_wtoi(wszTime+9);
        pTime->wMinute = (WORD)_wtoi(wszTime+12);
        pTime->wSecond = (WORD)_wtoi(wszTime+15);
        fRet = TRUE;
    }
    return fRet;
}
#endif

//
// IsInList() checks if a given item is in a list of items.
//
// Parameters:
// wszList: list of items (eg. "itemA,itemB,itemC")
// wszItem: item to search for
// wszSep: separator, for example a comma
//
// Return value: TRUE, if item is in list, otherwise FALSE
//
BOOL IsInList(const WCHAR *wszList, const char *szItem, const WCHAR *wszSep)
{
    WCHAR wszSearchList[MAX_ATCMD_LEN];
    WCHAR wszItem[MAX_ATCMD_LEN];
    WCHAR *wszToken;
    BOOL fIsInList = FALSE;

    (void)_snwprintfz(wszSearchList, MAX_ATCMD_LEN, L"%s", wszList);
    (void)_snwprintfz(wszItem, MAX_ATCMD_LEN, L"%S", szItem);
    wszToken = wcstok(wszSearchList, wszSep);
    while (wszToken != NULL && !fIsInList)
    {
        fIsInList = (wcsicmp(wszToken, wszItem)==0);
        wszToken = wcstok(NULL, L",");
    }
    return fIsInList;
}

// As above, but with WCHARs as parameters.
BOOL IsInListWCHAR(const WCHAR *wszList, const WCHAR *wszItem, const WCHAR *wszSep)
{
    WCHAR wszSearchList[MAX_ATCMD_LEN];
    WCHAR *wszToken;
    BOOL fIsInList = FALSE;

    (void)_snwprintfz(wszSearchList, MAX_ATCMD_LEN, L"%s", wszList);
    wszToken = wcstok(wszSearchList, wszSep);
    while (wszToken != NULL && !fIsInList)
    {
        fIsInList = (wcsicmp(wszToken, wszItem)==0);
        wszToken = wcstok(NULL, L",");
    }
    return fIsInList;
}

