#include "precomp.h"

typedef struct
{
    eKEYWORD eKey;
    char *szzAliases;   // Multi-sz string of aliases.
                        // First one is the "official" name.
} KEYWORDREC;

KEYWORDREC rgKeywords[] =
{
    {keywordHELP,           "help\0"},
    {keywordDUMP_TYPE,      "dt\0"},
    {keywordDUMP_GLOBALS,   "dg\0"},
    {keywordL,              "L\0"},
    {keywordNULL,           NULL}      // sentinel, must be last.
};


//
// Contains the list of tokens created by parsing an input string.
//
typedef struct
{
    TOKEN *rgToks;
    UINT cToks;
    UINT uNextFreeTok;
    UINT uCurrentTok;

    char *rgStringBuf;
    UINT cchStringBuf;
    UINT uNextFree;
    BOOL fFinalized;
    CRITICAL_SECTION crit;

} TOKLIST;


DBGCOMMAND *
parse_command(TOKLIST *pTL, NAMESPACE *pNameSpace);

TOKLIST
*toklist_create(void);

void
toklist_destroy(TOKLIST *pTL);

BOOL
toklist_add(TOKLIST *pTL, eTOKTYPE eTok, char *szOrig, UINT uID);

BOOL
toklist_finalize(TOKLIST *pTL);


TOKEN *
toklist_get_next(TOKLIST *pTL);

BOOL
toklist_restart(TOKLIST *pTL);

void
toklist_dump(TOKLIST *pTL);

void
tok_dump(TOKEN *pTok);


UINT
toklist_tokenize(TOKLIST *pTL, char *szInput);

UINT
toklist_parse_keyword(
      TOKLIST *pTL,
      KEYWORDREC rgKeywords[],
      char *pcInput
      );

UINT
toklist_parse_hexnum(
      TOKLIST *pTL,
      char *pcInput
      );

UINT
toklist_parse_identifier(
      TOKLIST *pTL,
      char *pcInput
      );

BOOL
cmd_parse_help(
    DBGCOMMAND *pCmd,
    TOKLIST *pTL
    );

BOOL
tok_try_force_to_ident(TOKLIST *pTL, BOOL fPrefixStar, TOKEN *pTok);

void
MyDumpObject (
    DBGCOMMAND *pCmd,
    TYPE_INFO *pType,
    UINT_PTR uAddr,
    UINT     cbSize,
    const char *szDescription
    );


ULONG
NodeFunc_DumpType (
	UINT_PTR uNodeAddr,
	UINT uIndex,
	void *pvContext
	);

ULONG
NodeFunc_UpdateCache (
	UINT_PTR uNodeAddr,
	UINT uIndex,
	void *pvContext
	);


DBGCOMMAND *
Parse(
    IN  const char *szInput,
    IN	NAMESPACE *pNameSpace
)
{
    TOKLIST *pTL = NULL;
    BOOL fRet = FALSE;
    DBGCOMMAND *pCmd = NULL;
    UINT cbInput =  (lstrlenA(szInput)+1)*sizeof(*szInput);
    char *szRWInput
        = LocalAlloc(LPTR, cbInput);

    // MyDbgPrintf("Parse(\"%s\");\n", szInput);

    if (szRWInput)
    {
        CopyMemory(szRWInput, szInput, cbInput);
        pTL =  toklist_create();
    }

    if (pTL)
    {

#if TEST_TOKLIST_ADD
    #if 0
        fRet = toklist_add(pTL, tokSTAR,        "*",        tokSTAR);
        fRet = toklist_add(pTL, tokDOT,         ".",        tokDOT);
        fRet = toklist_add(pTL, tokQUESTION,    "?",        tokQUESTION);
        fRet = toklist_add(pTL, tokLBRAC,       "[",        tokLBRAC);
        fRet = toklist_add(pTL, tokRBRAC,       "]",        tokRBRAC);
        fRet = toklist_add(pTL, tokSLASH,       "/",        tokSLASH);
        fRet = toklist_add(pTL, tokKEYWORD,     "help",     keywordHELP);
        fRet = toklist_add(pTL, tokNUMBER,      "0x1234",   0x1234);
        fRet = toklist_add(pTL, tokIDENTIFIER,  "cow",      0);
        fRet = toklist_add(pTL, tokSTAR,        "*",        tokSTAR);
        fRet = toklist_add(pTL, tokDOT,         ".",        tokDOT);
        fRet = toklist_add(pTL, tokQUESTION,    "?",        tokQUESTION);
        fRet = toklist_add(pTL, tokLBRAC,       "[",        tokLBRAC);
        fRet = toklist_add(pTL, tokRBRAC,       "]",        tokRBRAC);
        fRet = toklist_add(pTL, tokSLASH,       "/",        tokSLASH);
        fRet = toklist_add(pTL, tokKEYWORD,     "help",     keywordHELP);
        fRet = toklist_add(pTL, tokNUMBER,      "0x1234",   0x1234);
        fRet = toklist_add(pTL, tokIDENTIFIER,  "cow",      0);
    #else
        char rgInput[] =
                 // "*.?[]/"
                 // "help "
                 // "0x12340 0 1 02 "
                 // "kelp"
                "dt if[*].*handle* 0x324890 L 5"
                ;
        toklist_tokenize (pTL, rgInput);
    #endif

#endif // TEST_TOKLIST_ADD

        toklist_tokenize(pTL, szRWInput);

        toklist_finalize(pTL);

        // toklist_dump(pTL);

        pCmd = parse_command(pTL, pNameSpace);

        if (!pCmd)
        {
            toklist_destroy(pTL);
        }
        pTL = NULL;
    }

    if (szRWInput)
    {
        LocalFree(szRWInput);
        szRWInput = NULL;
    }

    return pCmd;

}

void
FreeCommand(
    DBGCOMMAND *pCmd
)
{
    if (pCmd)
    {
        TOKLIST *pTL =  (TOKLIST*)pCmd->pvContext;
        if (pTL)
        {
            // MyDbgPrintf("FreeCommand:\n");
            // toklist_restart(pTL);
            // toklist_dump(pTL);
            toklist_destroy((TOKLIST*)pCmd->pvContext);
        }

        ZeroMemory(pCmd, sizeof(*pCmd));
        LocalFree(pCmd);
    }
}

void
DumpCommand(
    DBGCOMMAND *pCmd
)
{
    char *szCmd = "";
    char *szObjPreStar = "";
    char *szObj = "";
    char *szObjSufStar = "";
    char *szObjVecRange = "";
    char *szDot = "";
    char *szSubObjPreStar = "";
    char *szSubObj = "";
    char *szSubObjSufStar = "";
    char *szObjAddr  = "";
    char *szObjCount = "";
    char rgVecRange[64];
    char rgObjAddr[64];
    char rgObjCount[64];


    if (!pCmd) goto end;

    switch(pCmd->ePrimaryCmd)
    {
    case cmdDUMP_TYPE:       szCmd = "dt"; break;
    case cmdDUMP_GLOBALS:    szCmd = "dg"; break;
    case cmdHELP:            szCmd = "help"; break;
    default:            szCmd = "<unknown>"; break;
    }

    if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_PREFIX))
    {
        szObjPreStar = "*";
    }
    if (pCmd->ptokObject)
    {
        szObj = pCmd->ptokObject->szStr;
    }

    if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_SUFFIX))
    {
        szObjSufStar = "*";
    }

    if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_HAS_VECTOR_INDEX))
    {
        wsprintfA(
            rgVecRange,
            "[%ld,%ld]",
            pCmd->uVectorIndexStart,
            pCmd->uVectorIndexEnd
            );

        szObjVecRange = rgVecRange;
    }

    if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_HAS_SUBOBJECT))
    {
        szDot = ".";
    }

    if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_SUBOBJECT_STAR_PREFIX))
    {
        szSubObjPreStar = "*";
    }

    if (pCmd->ptokSubObject)
    {
        szSubObj = pCmd->ptokSubObject->szStr;
    }

    if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_SUBOBJECT_STAR_SUFFIX))
    {
        szSubObjSufStar = "*";
    }

    if (CMD_IS_FLAG_SET(pCmd,  fCMDFLAG_HAS_OBJECT_ADDRESS))
    {
        wsprintf(rgObjAddr, "0x%lx", pCmd->uObjectAddress);
        szObjAddr = rgObjAddr;
    }

    if (CMD_IS_FLAG_SET(pCmd,  fCMDFLAG_HAS_OBJECT_COUNT))
    {
        wsprintf(rgObjCount, " L 0x%lx", pCmd->uObjectCount);
        szObjCount = rgObjCount;
    }

    {
    #if 0
        MyDbgPrintf(
            "\nCOMMAND = {"
            "cmd=%lu;"
            "F=0x%lx;"
            "O=0x%lx;"
            "SO=0x%lx;"
            "VS=%ld;"
            "VE=%ld;"
            "OA=0x%lx;"
            "OC=%ld;"
            "}\n",
            pCmd->ePrimaryCmd,
            pCmd->uFlags,
            pCmd->ptokObject,
            pCmd->ptokSubObject,
            pCmd->uVectorIndexStart,
            pCmd->uVectorIndexEnd,
            pCmd->uObjectAddress,
            pCmd->uObjectCount
            );
    #else
        MyDbgPrintf(
            "COMMAND = \"%s %s%s%s%s%s%s%s%s%s%s\";\n",
            szCmd,
            szObjPreStar,
            szObj,
            szObjSufStar,
            szObjVecRange,
            szDot,
            szSubObjPreStar,
            szSubObj,
            szSubObjSufStar,
            szObjAddr,
            szObjCount
        );
    #endif
    }
end:
    return;
}


#define TL_LOCK(_ptl)   EnterCriticalSection(&(_ptl)->crit)
#define TL_UNLOCK(_ptl) LeaveCriticalSection(&(_ptl)->crit)


TOKLIST
*toklist_create(void)
{
    TOKLIST *pTL = LocalAlloc(LPTR, sizeof(TOKLIST));

    if (pTL)
    {
        InitializeCriticalSection(&pTL->crit);
    }

    return pTL;
}


void
toklist_destroy(TOKLIST *pTL)
{
    if (pTL)
    {
        TL_LOCK(pTL);

        if (pTL->rgToks)
        {
            LocalFree(pTL->rgToks);
        }

        if (pTL->rgStringBuf)
        {
            LocalFree(pTL->rgStringBuf);
        }

        DeleteCriticalSection(&pTL->crit);

        ZeroMemory(pTL, sizeof(*pTL));
        LocalFree(pTL);
    }
}


BOOL
toklist_add(TOKLIST *pTL, eTOKTYPE eTok, char *szOrig, UINT uID)
{
    BOOL fRet = FALSE;
    TOKEN *pTok = NULL;
    UINT cch  = 0;
    char *pc  = NULL;

    TL_LOCK(pTL);

    if (pTL->fFinalized) goto end;

    //
    // Make sure we've enough space for the token.
    //
    if (pTL->uNextFreeTok >= pTL->cToks)
    {
        UINT cNewToks = 2*pTL->cToks+1;
        TOKEN *pNewToks = (TOKEN*) LocalAlloc(LPTR, cNewToks*sizeof(*pNewToks));
        if (!pNewToks) goto end;

        if (pTL->rgToks)
        {
            CopyMemory(
                pNewToks,
                pTL->rgToks,
                pTL->uNextFreeTok*sizeof(*pNewToks)
                );

            LocalFree(pTL->rgToks);
        }

        pTL->rgToks = pNewToks;
        pTL->cToks = cNewToks;
    }

    //
    // Now deal with szOrig
    //

    cch = lstrlenA(szOrig)+1;

    if ((pTL->uNextFree+cch+1) > pTL->cchStringBuf) // "+1" because multisz
    {
        UINT cNewStr = 2*pTL->cchStringBuf+cch+1;
        char *pNewStr = LocalAlloc(LPTR, cNewStr*sizeof(*pNewStr));
        if (!pNewStr) goto end;

        if (pTL->rgStringBuf)
        {
            CopyMemory(
                pNewStr,
                pTL->rgStringBuf,
                pTL->uNextFree*sizeof(*pNewStr)
                );
            LocalFree(pTL->rgStringBuf);

            //
            // Since we've reallocated the string buffer, we must
            // now fixup the string pointers in the list of tokens
            //
            {
                TOKEN *pTok = pTL->rgToks;
                TOKEN *pTokEnd = pTok + pTL->uNextFreeTok;
                for(; pTok<pTokEnd; pTok++)
                {
                    pTok->szStr = pNewStr + (pTok->szStr - pTL->rgStringBuf);
                }
            }
        }

        pTL->rgStringBuf = pNewStr;
        pTL->cchStringBuf = cNewStr;
    }

    //
    // At this point we know we have enough space...
    //

    //
    // See if we already have this string and if not copy it...
    //
    {
        BOOL fFound = FALSE;
        for (pc = pTL->rgStringBuf; *pc; pc+=(lstrlenA(pc)+1))
        {
            if (!lstrcmpiA(pc, szOrig))
            {
                // found it
                fFound = TRUE;
                break;
            }
        }


        if (!fFound)
        {
            MYASSERT(pTL->uNextFree == (UINT) (pc-pTL->rgStringBuf));

            CopyMemory(
                pc,
                szOrig,
                cch*sizeof(*szOrig)
                );
            pTL->uNextFree += cch;
        }
    }

    if (eTok == tokIDENTIFIER)
    {
        //
        // For this special case we ignore the passed-in uID and
        // use instead the offset of the string in our string table.
        //
        uID =  (UINT) (pc - pTL->rgStringBuf);
    }

    pTok = pTL->rgToks+pTL->uNextFreeTok++;
    pTok->eTok = eTok;
    pTok->uID = uID;
    pTok->szStr = pc;
    fRet = TRUE;

end:

    TL_UNLOCK(pTL);
    return fRet;
}


BOOL
toklist_finalize(TOKLIST *pTL)
{
    BOOL fRet = FALSE;

    TL_LOCK(pTL);

    if (pTL->fFinalized) goto end;

    pTL->fFinalized = TRUE;
    fRet = TRUE;

end:

    TL_UNLOCK(pTL);
    return fRet;
}

BOOL
toklist_restart(TOKLIST *pTL)
{
    BOOL fRet = FALSE;

    TL_LOCK(pTL);

    if (!pTL->fFinalized) goto end;
    pTL->uCurrentTok = 0;
    fRet = TRUE;

end:

    TL_UNLOCK(pTL);
    return fRet;
}


TOKEN *
toklist_get_next(TOKLIST *pTL)
{
    TOKEN *pTok = NULL;

    TL_LOCK(pTL);

    if (!pTL->fFinalized) goto end;

    if (pTL->uCurrentTok >= pTL->uNextFreeTok)
    {
        MYASSERT(pTL->uCurrentTok == pTL->uNextFreeTok);
        goto end;
    }
    else
    {
        pTok = pTL->rgToks+pTL->uCurrentTok++;
    }

end:
    TL_UNLOCK(pTL);


    return pTok;
}

void
toklist_dump(TOKLIST *pTL)
{
    TL_LOCK(pTL);

    MyDbgPrintf(
            "\nTOKLIST 0x%08lx = {"
            "fFin=%lu cToks=%lu  uNextFreeTok=%lu cchStr=%lu uNextFree=%lu"
            "}\n",
            pTL,
            pTL->fFinalized,
            pTL->cToks,
            pTL->uNextFreeTok,
            pTL->cchStringBuf,
            pTL->uNextFree
        );

    if (pTL->fFinalized)
    {
        TOKEN *pTok =  toklist_get_next(pTL);
        while(pTok)
        {
            tok_dump(pTok);

            pTok =  toklist_get_next(pTL);
        }
        toklist_restart(pTL);
    }

    TL_UNLOCK(pTL);
}


void
tok_dump(TOKEN *pTok)
{
    MyDbgPrintf(
            "\tTOKEN 0x%08lx = {eTok=%lu uID=0x%08lx sz=\"%s\"}\n",
            pTok,
            pTok->eTok,
            pTok->uID,
            pTok->szStr
        );

}


UINT
toklist_tokenize(TOKLIST *pTL, char *szInput)
{
    UINT cTokens = 0;
    char *pc = szInput;
    char c = 0;
    BOOL fRet = FALSE;

    for (; (c=*pc)!=0; pc++)
    {
        switch(c)
        {

        case '*':
            fRet = toklist_add(pTL, tokSTAR,        "*",        tokSTAR);
            continue;

        case '.':
            fRet = toklist_add(pTL, tokDOT,         ".",        tokDOT);
            continue;

        case '?':
            fRet = toklist_add(pTL, tokQUESTION,    "?",        tokQUESTION);
            continue;

        case '[':
            fRet = toklist_add(pTL, tokLBRAC,       "[",        tokLBRAC);
            continue;

        case ']':
            fRet = toklist_add(pTL, tokRBRAC,       "]",        tokRBRAC);
            continue;

        case '/':
            fRet = toklist_add(pTL, tokSLASH,       "/",        tokSLASH);
            continue;

        case '\n':
        case '\r':
        case '\t':
        case ' ':
            continue;

        default:

            {
                UINT uCharsParsed =  0;
                char *pcEnd = pc;
                char cSave = 0;

                //
                // We'll locate the end of the potential keyword/number/ident:
                // and temprarily place a NULL char there.
                //
                //
                while (__iscsym(*pcEnd))
                {
                    pcEnd++;
                }

                cSave = *pcEnd;
                *pcEnd = 0;

                if (__iscsymf(c))
                {
                    // This may be a keyword, hex number or identifier. We try
                    // in this order
                    uCharsParsed =  toklist_parse_keyword(
                                                pTL,
                                                rgKeywords,
                                                pc
                                                );

                    if (!uCharsParsed && isxdigit(c))
                    {
                        //
                        // Didn't find a keyword and this is a hex digit --
                        // let's try to parse it as a hex number...
                        //
                        uCharsParsed =  toklist_parse_hexnum(pTL, pc);
                    }

                    if (!uCharsParsed)
                    {
                        //
                        // Parse it as an identifier...
                        //
                        uCharsParsed =  toklist_parse_identifier(pTL, pc);
                    }

                    if (!uCharsParsed)
                    {
                        //
                        // This is an error
                        //
                        MyDbgPrintf("Error at %s\n", pc);
                        goto end;
                    }
                }
                else if (isxdigit(c))
                {
                   uCharsParsed =  toklist_parse_hexnum(pTL, pc);
                }

                //
                // If we've parsed anything it should be ALL of the string...
                //
                MYASSERT(!uCharsParsed || uCharsParsed==(UINT)lstrlenA(pc));

                //
                // Restore the char we replaced by NULL.
                //
                *pcEnd = cSave;

                if (!uCharsParsed)
                {
                    //
                    // Syntax error
                    //
                    MyDbgPrintf("Error at %s\n", pc);
                    goto end;
                }
                else
                {
                    pc+= (uCharsParsed-1); // "-1" because of pc++ in
                                            // for clause above.
                }
            }
        }
    }

end:

return cTokens;

}

UINT
toklist_parse_keyword(
      TOKLIST *pTL,
      KEYWORDREC rgKeywords[],
      char *pcInput
      )
//
// Assumes 1st char is valid.
//
{
    UINT uRet = 0;
    KEYWORDREC *pkr = rgKeywords;

    if (!__iscsymf(*pcInput)) goto end;

    for (;pkr->eKey!=keywordNULL; pkr++)
    {
        if (!lstrcmpi(pcInput, pkr->szzAliases))
        {
            //
            // found it
            //
            toklist_add(pTL, tokKEYWORD,  pcInput,  pkr->eKey);
            uRet = lstrlenA(pcInput);
            break;
        }
    }

end:

    return uRet;
}

UINT
toklist_parse_hexnum(
      TOKLIST *pTL,
      char *pcInput
      )
{
    char *pc = pcInput;
    UINT uValue = 0;
    char c;
    UINT u;

    //
    //  look for and ignore the "0x" prefix...
    //
    if (pc[0]=='0' && (pc[1]=='x' || pc[1]=='X'))
    {
        pc+=2;
    }


    //
    // Reject number if it is doesn't contain hex digits or is too large
    //
    for (u=0; isxdigit(*pc) && u<8; pc++,u++)
    {
        UINT uDigit = 0;

        char c = *pc;
        if (!isdigit(c))
        {
            c = (char) _toupper(c);
            uDigit = 10 + c - 'A';
        }
        else
        {
            uDigit = c - '0';
        }

        uValue = (uValue<<4)|uDigit;
    }

    if (!u || *pc)
    {
        return 0;
    }
    else
    {
        toklist_add(pTL, tokNUMBER, pcInput, uValue);
        return pc - pcInput;
    }
}

UINT
toklist_parse_identifier(
      TOKLIST *pTL,
      char *pcInput
      )
{
    UINT uRet = 0;

    if (!__iscsymf(*pcInput)) goto end;

    toklist_add(pTL, tokIDENTIFIER,  pcInput,  0);
    uRet = lstrlenA(pcInput);

end:

    return uRet;
}

DBGCOMMAND *
parse_command(TOKLIST *pTL, NAMESPACE *pNameSpace)
{
    BOOL fRet = FALSE;
    DBGCOMMAND *pCmd = LocalAlloc(LPTR, sizeof(*pCmd));
    TOKEN *pTok =  NULL;
    BOOL fSyntaxError = FALSE;

    if (!pCmd) goto end;

    toklist_restart(pTL);
    pTok =  toklist_get_next(pTL);

    if (!pTok) goto end;

	pCmd->pNameSpace = pNameSpace;
    //
    // Now let's step through the token list, building up our command
    // information.
    //

    // look for help or ?
    if (pTok->eTok == tokQUESTION
       || (pTok->eTok == tokKEYWORD && pTok->uID == keywordHELP))
    {
        pCmd->ePrimaryCmd = cmdHELP;
        fRet = cmd_parse_help(pCmd, pTL);
        goto end;
    }

    fSyntaxError = TRUE;
    fRet = FALSE;

	//
	// Here we would look for other keywords. Currently there are none
	// (dt and dg are not used anymore).
	//
	//
	#if OBSOLETE
    if (pTok->eTok == tokKEYWORD)
    {
           BOOL fDump = FALSE;
		if (pTok->uID == keywordDUMP_TYPE)
        {
            pCmd->ePrimaryCmd = cmdDUMP_TYPE;
            fDump = TRUE;
        }
        else if (pTok->uID == keywordDUMP_GLOBALS)
        {
            pCmd->ePrimaryCmd = cmdDUMP_GLOBALS;
            fDump = TRUE;
        }
        ...
	}
	#endif // OBSOLETE

	pCmd->ePrimaryCmd = cmdDUMP_TYPE;

	//
	// Pares the form a[b].*c* d L e
	//
	{

		BOOL   fPrefixStar = FALSE;
		// we look for patterns like...
		//!aac <type> . <field> <address> L <count> <flags>
		//!aac <type> [index] . <field>   L <count> <flags>
		//
		//!aac i[*].*handle* 0x324890 L 5
		//[*]ident[*]\[<range>\][.][*]ident[*] <number> [L <number>]

		UINT uFlags;            // One or more fCMDFLAG_*
		TOKEN *ptokObject;     // eg <type>
		TOKEN *ptokSubObject;  // eg <field>
		UINT uVectorIndexStart; // if[0]
		UINT uVectorIndexEnd; // if[0]
		UINT uObjectAddress; // <address>
		UINT uObjectCount; // L 10
	
		//
		// 1. Look for primary star
		//
		if (pTok && pTok->eTok == tokSTAR)
		{
			fPrefixStar = TRUE;
			CMD_SET_FLAG(pCmd, fCMDFLAG_OBJECT_STAR_PREFIX);
			pTok = toklist_get_next(pTL);
		}

		//
		// 2.  Look for ident
		//
		if (pTok && tok_try_force_to_ident(pTL, fPrefixStar, pTok))
		{
			//
			// This will try to convert keywords and numbers to idents if
			// possible.
			//
			pCmd->ptokObject = pTok;
			pTok = toklist_get_next(pTL);
		}

		//
		// 3. Look for suffix * for object.
		//
		if (pTok && pTok->eTok == tokSTAR)
		{
			CMD_SET_FLAG(pCmd, fCMDFLAG_OBJECT_STAR_SUFFIX);
			pTok = toklist_get_next(pTL);
		}

		//
		// 4. Look for Vector Range
		//
		if (pTok && pTok->eTok == tokLBRAC)
		{
			//
			// For now, we support either a single * or a single number.
			//
			pTok = toklist_get_next(pTL);

			if (!pTok)
			{
				goto end; // Error -- incomplete vector range
			}
			else
			{
				if (pTok->eTok == tokSTAR)
				{
					pCmd->uVectorIndexStart = 0;
					pCmd->uVectorIndexEnd = (UINT) -1;
				}
				else if (pTok->eTok == tokNUMBER)
				{
					pCmd->uVectorIndexStart =
					pCmd->uVectorIndexEnd = pTok->uID;
				}
				else
				{
					goto end; // failure...
				}

				CMD_SET_FLAG(pCmd, fCMDFLAG_HAS_VECTOR_INDEX);

				pTok = toklist_get_next(pTL);

				if (!pTok || pTok->eTok != tokRBRAC)
				{
					goto end; // failure ... expect RBRAC.
				}
				else
				{
					pTok = toklist_get_next(pTL);
				}
			}
		}

		//
		// 5. Look for DOT
		//
		if (pTok && pTok->eTok == tokDOT)
		{
			fPrefixStar = FALSE;
			pTok = toklist_get_next(pTL);

			// We expect ([*]ident[*]|*)
			//
			// 1. Look for primary star
			//
			if (pTok && pTok->eTok == tokSTAR)
			{
				fPrefixStar = TRUE;
				CMD_SET_FLAG(pCmd, fCMDFLAG_SUBOBJECT_STAR_PREFIX);
				pTok = toklist_get_next(pTL);
			}

			//
			// 2.  Look for ident
			//
			if (pTok && tok_try_force_to_ident(pTL, fPrefixStar, pTok))
			{
				//
				// This will try to convert keywords and numbers to idents if
				// possible.
				//
				pCmd->ptokSubObject = pTok;
				pTok = toklist_get_next(pTL);
			}

			//
			// 3. Look for suffix * for object.
			//
			if (pTok && pTok->eTok == tokSTAR)
			{
				CMD_SET_FLAG(pCmd, fCMDFLAG_SUBOBJECT_STAR_SUFFIX);
				pTok = toklist_get_next(pTL);
			}

			//
			// At this point we should either have a non-null IDENT
			// or the PREFIX START should be set for the object
			// (indicateing "a.*").
			//
			if (    pCmd->ptokSubObject
 				|| (pCmd->uFlags & fCMDFLAG_SUBOBJECT_STAR_SUFFIX))
			{
				CMD_SET_FLAG(pCmd, fCMDFLAG_HAS_SUBOBJECT);
			}
			else
			{
				goto end; // error
			}
		}

		//
		// 6. Look for object address
		//
		if (pTok && pTok->eTok == tokNUMBER)
		{
			pCmd->uObjectAddress = pTok->uID;
			CMD_SET_FLAG(pCmd, fCMDFLAG_HAS_OBJECT_ADDRESS);
			pTok = toklist_get_next(pTL);
		}

		//
		// 7. Look for object count
		//
		if (   pTok && pTok->eTok == tokKEYWORD
			&& pTok->uID == keywordL)
		{
			pTok = toklist_get_next(pTL);
			if (pTok && pTok->eTok == tokNUMBER)
			{
				pCmd->uObjectCount = pTok->uID;
				CMD_SET_FLAG(pCmd, fCMDFLAG_HAS_OBJECT_COUNT);
				pTok = toklist_get_next(pTL);
			}
			else
			{
				// error
			}
		}

		//
		// At this point we should be done...
		//
		if (pTok)
		{
			// error -- extra garbage...
		}
		else
		{
			// Success.
			fRet = TRUE;
			fSyntaxError = FALSE;
		}
	}

end:

    if (fRet)
    {
        pCmd->pvContext = pTL;
    }
    else
    {
        if (fSyntaxError)
        {
            MyDbgPrintf("Unexpected: %s\n", (pTok) ? pTok->szStr : "<null>");
        }
        else
        {
            MyDbgPrintf("Parse failed\n");
        }

        if (pCmd)
        {
            ZeroMemory(pCmd, sizeof(*pCmd));
            LocalFree(pCmd);
            pCmd = NULL;
        }
    }

    if (pTL)
    {
        toklist_restart(pTL);
    }

    return pCmd;
}

BOOL
cmd_parse_help(
    DBGCOMMAND *pCmd,
    TOKLIST *pTL
    )
{
    TOKEN *pTok = toklist_get_next(pTL);

    if (!pTok || pTok->eTok == tokSTAR)
    {
        // User type "help" or "help *"
        MyDbgPrintf("DO HELP\n");
    }

    return TRUE;
}

BOOL
tok_try_force_to_ident(TOKLIST *pTL, BOOL fPrefixStar, TOKEN *pTok)
//
// This gets called when an identifier is expected -- so we see if this
// particular token can be interpreted as in identifier. Some examples
// of when we can do this:
//  dt if.*20334     <--- the "20334" could be part of an identifier, because
//                        of the * prefix.
//
//  dt L.help        <--- both "L" and "help" would have been parsed as
//                        keywords, but here they are intended to be
//                        identifiers.
//  dt abc.def       <--- abc and def would have been parsed as numbers (they
//                        are valid hex numbers), but are intended to be
//                        identifiers.
{
    BOOL fRet = FALSE;

    switch(pTok->eTok)
    {

    case tokNUMBER:
        //
        // We could do this, but subject to some restrictions...
        //
        if (!__iscsymf(pTok->szStr[0]) &&  !fPrefixStar)
        {
            break; // Can't to this: no prefix wild-card (*) and the
                   // number starts with a non-letter.
        }

        // FALL THROUGH ...

    case tokKEYWORD:
        //
        // We can go ahead, but we must make pTok.uID now the offset
        // from the start of the internal string array.
        //
        {
            char *pc = pTL->rgStringBuf;

            for (; *pc; pc+=(lstrlenA(pc)+1))
            {
                if (!lstrcmpiA(pc, pTok->szStr))
                {
                    // found it
                    // MyDbgPrintf("FORCE_TO_IDENT:\nOLD:\n");
                    // tok_dump(pTok);
                    pTok->uID =  (UINT) (pc - pTL->rgStringBuf);
                    pTok->eTok = tokIDENTIFIER;
                    // MyDbgPrintf("NEW:\n");
                    // tok_dump(pTok);
                    fRet = TRUE;

                    break;
                }
            }
        }
        break;

    case tokIDENTIFIER:
        //
        // nothing to do...
        //
        fRet = TRUE;
        break;

    default:
        //
        // Can't convert any other kind of token to identifier...
        //
        break;

    }

    return fRet;
}

void
DoCommand(DBGCOMMAND *pCmd, PFN_SPECIAL_COMMAND_HANDLER pfnHandler)
{
    char *szMsg = NULL;

//    pCmd->pfnSpecialHandler = pfnHandler;

    switch(pCmd->ePrimaryCmd)
    {
    case cmdDUMP_TYPE:
        DoDumpType(pCmd);
        break;
    case cmdDUMP_GLOBALS:
        DoDumpGlobals(pCmd);
        break;
    case cmdHELP:
        DoHelp(pCmd);
        break;

    default:
        szMsg = "Unknown command\n";
        break;
    }


    if (szMsg)
    {
        MyDbgPrintf(szMsg);
    }

    return;
}


typedef struct
{
    DBGCOMMAND *pCmd;
    TYPE_INFO  *pType;

} MY_LIST_NODE_CONTEXT;

typedef
ULONG
MyDumpListNode (
		UINT_PTR uNodeAddr,
		UINT uIndex,
		void *pvContext
		);

void
DoDumpType(DBGCOMMAND *pCmd)
{
    char *szPattern = NULL;
    PFNMATCHINGFUNCTION pfnMatchingFunction = MatchAlways;
    TYPE_INFO **ppti = NULL;
    UINT uMatchCount = 0;
    TYPE_INFO *ptiDump = NULL;

    //
    // Pick a selection function ...
    //
    if (pCmd->ptokObject)
    {
        szPattern = pCmd->ptokObject->szStr;
        if (  CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_PREFIX)
            &&CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_SUFFIX))
        {
            pfnMatchingFunction = MatchSubstring;
        }
        else if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_PREFIX))
        {
            pfnMatchingFunction = MatchSuffix;
        }
        else if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_SUFFIX))
        {
            pfnMatchingFunction = MatchPrefix;
        }
        else
        {
            pfnMatchingFunction = MatchExactly;
        }

    }

    //
    // search through global type array for type pName.
    //
    for(ppti=pCmd->pNameSpace->pTypes;*ppti;ppti++)
    {
        TYPE_INFO *pti = *ppti;
        bool fMatch  = !szPattern
                   || !_stricmp(szPattern, pti->szShortName)
                   || pfnMatchingFunction(szPattern,  pti->szName);

        if (fMatch)
        {
        #if 0
            MyDbgPrintf(
                "TYPE \"%2s\" %s (%lu Bytes)\n",
                pti->szShortName,
                pti->szName,
                pti->cbSize
                );
		#endif // 0
            uMatchCount++;
            if (!ptiDump)
            {
                ptiDump = pti;
            }

#if 0
            uAddr =
            MyDbgPrintf(
                "dc 0x%08lx L %03lx \"%2s\" %s\n",
                pgi->uAddr,
                pgi->cbSize,
                pgi->szShortName,
                pgi->szName
                );
            if (szPattern && pgi->uAddr)
            {
                MyDumpObject(
                    pCmd,
                    pgi->pBaseType,
                    pgi->uAddr,
                    pgi->cbSize,
                    pgi->szName
                    );
            }
#endif // 0
        }
    }

    if (!uMatchCount)
    {
        MyDbgPrintf(
            "Could not find type \"%s\"",
             (szPattern ? szPattern : "*")
             );
    }
    else if (   uMatchCount==1)
    {

		UINT uObjectCount = 1;
		UINT uStartIndex = 0;
		UINT uObjectAddress = 0;
		BOOLEAN fList =  TYPEISLIST(ptiDump)!=0;

    	if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_HAS_OBJECT_ADDRESS))
    	{
			uObjectAddress = pCmd->uObjectAddress;
		}

		//
		// Determine start index.
		//
		if (CMD_IS_FLAG_SET(pCmd,  fCMDFLAG_HAS_VECTOR_INDEX))
		{
			uStartIndex =  pCmd->uVectorIndexStart;
			if (fList && !CMD_IS_FLAG_SET(pCmd, fCMDFLAG_HAS_OBJECT_COUNT))
			{
				uObjectCount =  pCmd->uVectorIndexEnd - uStartIndex;
				if (uObjectCount != (UINT) -1)
				{
					uObjectCount++;
				}
			}
		}

		//
		// Determine object count...
		//
		if (CMD_IS_FLAG_SET(pCmd,  fCMDFLAG_HAS_OBJECT_COUNT))
		{
			uObjectCount =  pCmd->uObjectCount;
		}

		//
		// If no address is specified, we'll try to resolve it ...
		//
    	if (!CMD_IS_FLAG_SET(pCmd, fCMDFLAG_HAS_OBJECT_ADDRESS))
    	{
    		BOOLEAN fUseCache = FALSE;

			//
			// Algorithm for determining whether to use cache or to resolve
			// address:
			//
			if (ptiDump->uCachedAddress)
			{
				//
				// Except for the special case of [0], we will use
				// the the cached value.
				//
				if (!(		uStartIndex ==0
					 	&& 	uObjectCount==1
					 	&&  CMD_IS_FLAG_SET(pCmd,  fCMDFLAG_HAS_VECTOR_INDEX)))
				{
					fUseCache = TRUE;
				}
			}

			if (fUseCache)
			{
				uObjectAddress = ptiDump->uCachedAddress;
			}
			else
			{
				if (pCmd->pNameSpace->pfnResolveAddress)
				{
					uObjectAddress = pCmd->pNameSpace->pfnResolveAddress(
														ptiDump
														);
				}
			}
    	}

    	if (uObjectAddress && uObjectCount)
    	{

			//
			// Prune these to "reasonable" values.
			//
			if (uObjectCount > 100)
			{
				MyDbgPrintf("Limiting object count to 100\n");
				uObjectCount = 100;
			}

			if (fList)
			{
				MY_LIST_NODE_CONTEXT Context;
				Context.pCmd = pCmd;
				Context.pType = ptiDump;

				WalkList(
					uObjectAddress,		// start address
					ptiDump->uNextOffset, 		// next offset
					uStartIndex,
					uStartIndex+uObjectCount-1, // end index
					&Context,					// context
					NodeFunc_DumpType,			// function
					(char *) ptiDump->szName
					);

				//
				// If only a single structure was dumped, and it was dumped
				// successfully, we will update this structure's cache.
				// TODO: we don't check for success
				//
				if (uObjectCount==1)
				{
					WalkList(
						uObjectAddress,			// start address
						ptiDump->uNextOffset, 	// next offset
						uStartIndex,
						uStartIndex,  			// end index
						ptiDump,					// context
						NodeFunc_UpdateCache,	// function
						(char *) ptiDump->szName
						);
				}
			}
			else
			{
				UINT cbSize =  ptiDump->cbSize;
				UINT uAddr  =  uObjectAddress + uStartIndex*cbSize;
				UINT uEnd   =  uAddr + uObjectCount*cbSize;
				//
				// For arays, compute offset to start address
				//
				uObjectAddress = uAddr;

				for (; uAddr<uEnd; uAddr+=cbSize)
				{
					MyDumpObject(
						pCmd,
						ptiDump,
						uAddr,
						ptiDump->cbSize,
						ptiDump->szName
						);
				}
				//
				// If only a single structure was dumped, and it was dumped
				// successfully, we will update this structure's cache.
				// TODO: we don't check for success
				//
				if (uObjectCount==1)
				{
					ptiDump->uCachedAddress = uObjectAddress;
				}
			}

    	}
    	else
    	{
    		MyDbgPrintf(
				"Could not resolve address for object %s\n",
				ptiDump->szName
				);
    	}
    }

}

void
DoDumpGlobals(DBGCOMMAND *pCmd)
{
    GLOBALVAR_INFO *pgi = pCmd->pNameSpace->pGlobals;
    char *szPattern = NULL;
    PFNMATCHINGFUNCTION pfnMatchingFunction = MatchAlways;

    //
    // Pick a selection function ...
    //
    if (pCmd->ptokObject)
    {
        szPattern = pCmd->ptokObject->szStr;
        if (  CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_PREFIX)
            &&CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_SUFFIX))
        {
            pfnMatchingFunction = MatchSubstring;
        }
        else if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_PREFIX))
        {
            pfnMatchingFunction = MatchSuffix;
        }
        else if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_OBJECT_STAR_SUFFIX))
        {
            pfnMatchingFunction = MatchPrefix;
        }
        else
        {
            pfnMatchingFunction = MatchExactly;
        }

    }

    //
    // Run through our list of globals, and if the entry is selected,
    // we will display it.
    //
    for (;pgi->szName; pgi++)
    {
        bool fMatch  = !szPattern
                       || !_stricmp(szPattern, pgi->szShortName)
                       || pfnMatchingFunction(szPattern,  pgi->szName);
        if (fMatch)
        {
            pgi->uAddr = dbgextGetExpression(pgi->szName);
            MyDbgPrintf(
                "dc 0x%08lx L %03lx \"%2s\" %s\n",
                pgi->uAddr,
                pgi->cbSize,
                pgi->szShortName,
                pgi->szName
                );
            if (szPattern && pgi->uAddr)
            {
                MyDumpObject(
                    pCmd,
                    pgi->pBaseType,
                    pgi->uAddr,
                    pgi->cbSize,
                    pgi->szName
                    );
            }
        }
    }
}

void
DoHelp(
	DBGCOMMAND *pCmd // OPTIONAL
	)
{
	//
	//
	//
    MyDbgPrintf("help unimplemented\n");
}

void
MyDumpObject (
    DBGCOMMAND *pCmd,
    TYPE_INFO *pType,
    UINT_PTR uAddr,
    UINT     cbSize,
    const char *szDescription
    )
{
    UINT uMatchFlags = 0;
    char *szFieldSpec  = NULL;

    if (pCmd->ptokSubObject)
    {
        szFieldSpec = pCmd->ptokSubObject->szStr;
        if (  CMD_IS_FLAG_SET(pCmd, fCMDFLAG_SUBOBJECT_STAR_PREFIX)
            &&CMD_IS_FLAG_SET(pCmd, fCMDFLAG_SUBOBJECT_STAR_SUFFIX))
        {
            uMatchFlags = fMATCH_SUBSTRING;
        }
        else if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_SUBOBJECT_STAR_PREFIX))
        {
            uMatchFlags = fMATCH_SUFFIX;
        }
        else if (CMD_IS_FLAG_SET(pCmd, fCMDFLAG_SUBOBJECT_STAR_SUFFIX))
        {
            uMatchFlags = fMATCH_PREFIX;
        }
    }

    if (!pType)
    {
        DumpMemory(
            uAddr,
            cbSize,
            0,
            szDescription
            );
    }
    else
    {
        DumpStructure(pType, uAddr, szFieldSpec, uMatchFlags);
    }
}

ULONG
NodeFunc_DumpType (
	UINT_PTR uNodeAddr,
	UINT uIndex,
	void *pvContext
	)
{
	MY_LIST_NODE_CONTEXT *pContext =  (MY_LIST_NODE_CONTEXT*) pvContext;

	MyDbgPrintf("[%lu] ", uIndex);
	MyDumpObject (
		pContext->pCmd,
		pContext->pType,
		uNodeAddr,
		pContext->pType->cbSize,
		pContext->pType->szName
		);
	return 0;
}

ULONG
NodeFunc_UpdateCache (
	UINT_PTR uNodeAddr,
	UINT uIndex,
	void *pvContext
	)
{
	TYPE_INFO *pti = (TYPE_INFO*) pvContext;

	if (pti->uCachedAddress != uNodeAddr)
	{
		MyDbgPrintf(
			"Updating Cache from 0x%lx to 0x%lx\n",
			pti->uCachedAddress,
			uNodeAddr
			);
	}
	pti->uCachedAddress = uNodeAddr;
	return 0;
}