/****************************** Module Header ******************************\
* Module Name: stdexts.c
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This module contains standard routines for creating sane debuging extensions.
* It is meant to be included after stdexts.h in one of the files comprising
* the debug extsnsions for a given product or module.
*
* History:
* 11-Apr-1995 Sanfords  Created
\***************************************************************************/

HANDLE                  hCurrentProcess;
HANDLE                  hCurrentThread;
DWORD                   dwCurrentPc;
WINDBG_EXTENSION_APIS  *lpExtensionApis;
#ifdef KERNEL
DWORD                   dwProcessor;
#endif // KERNEL

PSTR pszAccessViolation = "%s: Access violation on \"%s\".\n";
PSTR pszMoveException   = "%s: exception in moveBlock()\n";
PSTR pszReadFailure     = "%s: lpReadProcessMemoryRoutine failed!\n";
PSTR pszCantContinue    = "%s: Non-continuable exception.\n";
BOOL fCtrlCHit = FALSE;


/*
 * This function returns TRUE once the user has hit a Ctrl-C.
 * This allows proper operation of nested SAFEWHILE loops so
 * that all levels exit.
 *
 * The globall fCtrlCHit flag needs to be reset manually and
 * is done so in the CommandEP function.
 */
BOOL IsCtrlCHit()
{
    if ((lpExtensionApis->lpCheckControlCRoutine)()) {
        fCtrlCHit = TRUE;
    }
    return fCtrlCHit;
}



VOID moveBlock(
PVOID pdst,
PVOID src,
DWORD size)
{
    BOOL fSuccess = TRUE;
    ULONG Result;

    try {
        if (IsWinDbg()) {
            if (!ReadMem((DWORD_PTR)src, pdst, size, &Result)) {
                fSuccess = FALSE;
             }
        } else {
            if (!NT_SUCCESS(NtReadVirtualMemory(hCurrentProcess,
                    src, pdst, size, NULL))) {
                fSuccess = FALSE;
            }
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {
        Print(pszMoveException, pszExtName);
        fSuccess = FALSE;
    }
    if (!fSuccess) {
        DEBUGPRINT("%s: moveBlock(%p, %p, %x) failed.\n",
                pszExtName, pdst, src, size);
        OUTAHERE();
    }
}



BOOL tryMoveBlock(
PVOID pdst,
PVOID src,
DWORD size)
{
    BOOL fSuccess = TRUE;
    ULONG Result;

    try {
        if (IsWinDbg()) {
            if (!ReadMem((DWORD_PTR)src, pdst, size, &Result)) {
                DEBUGPRINT("%s: tryMoveBlock(%p, %p, %x) failed.\n", pszExtName, pdst, src, size);
                fSuccess = FALSE;
             }
        } else {
            if (!NT_SUCCESS(NtReadVirtualMemory(hCurrentProcess, src, pdst, size, NULL))) {
                DEBUGPRINT("%s: tryMoveBlock(%p, %p, %x) failed.\n", pszExtName, pdst, src, size);
                fSuccess = FALSE;
            }
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {
        DEBUGPRINT("%s: tryMoveBlock(%p, %p, %x) faulted.\n", pszExtName, pdst, src, size);
        fSuccess = FALSE;
    }
    return(fSuccess);
}



VOID moveExp(
PVOID pdst,
LPSTR pszExp)
{
    DWORD_PTR dwGlobal;
    BOOL fSuccess = TRUE;

    try {
        dwGlobal = (DWORD_PTR)EvalExp(pszExp);
#ifndef KERNEL
        if (IsWinDbg()) {
            fSuccess = tryMoveBlock(&dwGlobal, (PVOID)dwGlobal, sizeof(DWORD));
        }
#endif // !KERNEL
        *((PDWORD_PTR)pdst) = dwGlobal;
    } except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
                EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        Print(pszAccessViolation, pszExtName, pszExp);
        fSuccess = FALSE;
    }
    if (!fSuccess) {
        Print("%s: moveExp failed on %s.\n", pszExtName, pszExp);
        OUTAHERE();
    }
}


BOOL tryMoveExp(
PVOID pdst,
LPSTR pszExp)
{
    DWORD_PTR dwGlobal;
    BOOL fSuccess = TRUE;

    try {
        dwGlobal = (DWORD_PTR)EvalExp(pszExp);
#ifndef KERNEL
        if (IsWinDbg()) {
            if (!tryMove(dwGlobal, (PVOID)dwGlobal)) {
                DEBUGPRINT("%s: tryMoveExp(%p, %s) failed.\n", pszExtName, pdst, pszExp);
                fSuccess = FALSE;
            }
        }
#endif // !KERNEL
        *((PDWORD_PTR)pdst) = dwGlobal;
    } except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
                EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        Print(pszAccessViolation, pszExtName, pszExp);
        DEBUGPRINT("%s: tryMoveExp(%p, %s) faulted.\n", pszExtName, pdst, pszExp);
        fSuccess = FALSE;
    }
    return(fSuccess);
}


VOID moveExpValue(
PVOID pdst,
LPSTR pszExp)
{
    DWORD dw;
    DWORD_PTR addr;

    if (tryMoveExp(&addr, pszExp)) {
        if (tryMoveBlock(&dw, (PVOID)addr, sizeof(DWORD))) {
            *((PDWORD)pdst) = dw;
            return;
        }
    }
    Print("%s: moveExpValue failed on %s.\n", pszExtName, pszExp);
    OUTAHERE();
}


BOOL tryMoveExpValue(
PVOID pdst,
LPSTR pszExp)
{
    DWORD dw;
    DWORD_PTR addr;

    if (tryMoveExp(&addr, pszExp)) {
        if (tryMove(dw, (PVOID)addr)) {
            *((PDWORD)pdst) = dw;
            return(TRUE);
        }
    }
    DEBUGPRINT("%s: tryMoveExpValue failed on %s.\n", pszExtName, pszExp);
    return(FALSE);
}


BOOL tryMoveExpPtr(
PVOID pdst,
LPSTR pszExp)
{
    DWORD_PTR dwGlobal;
    BOOL fSuccess = TRUE;

    try {
        dwGlobal = (DWORD_PTR)EvalExp(pszExp);
#ifndef KERNEL
        if (IsWinDbg()) {
            if (!tryMove(dwGlobal, (PVOID)dwGlobal)) {
                DEBUGPRINT("%s: tryMoveExpPtr(%p, %s) failed.\n", pszExtName, pdst, pszExp);
                fSuccess = FALSE;
            }
        }
#endif // !KERNEL
        *((PDWORD_PTR)pdst) = dwGlobal;
    } except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
                EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        Print(pszAccessViolation, pszExtName, pszExp);
        DEBUGPRINT("%s: tryMoveExpPtr(%p, %s) faulted.\n", pszExtName, pdst, pszExp);
        fSuccess = FALSE;
    }
    return(fSuccess);
}


VOID moveExpValuePtr(
PVOID pdst,
LPSTR pszExp)
{
    DWORD_PTR dw;

    if (tryMoveExpPtr(&dw, pszExp)) {
        if (tryMoveBlock(&dw, (PVOID)dw, sizeof(dw))) {
            *((PDWORD_PTR)pdst) = dw;
            return;
        }
    }
    Print("%s: moveExpValue failed on %s.\n", pszExtName, pszExp);
    OUTAHERE();
}


/***************************************************************************
 * Common command parsing stuff                                            *
 ***************************************************************************/
PVOID EvalExp(
LPSTR psz)
{
    PVOID p;

    p = (PVOID)(lpExtensionApis->lpGetExpressionRoutine)(psz);
    if (p == NULL) {
        Print("%s: EvalExp failed to evaluate %s.\n", pszExtName, psz);
    }
    return p;
}



PVOID OptEvalExp(
LPSTR psz)
{
    while (*psz == ' ')
        psz++;
    if (*psz == '\0') {
        return(NULL);
    }
    return(EvalExp(psz));
}



PVOID OptEvalExp2(
LPSTR *ppsz)
{
    LPSTR psz = *ppsz;
    PVOID dwRet = NULL;

    while (*psz == ' ')
        psz++;
    if (*psz != '\0') {
        dwRet = EvalExp(psz);
        while (*psz != '\0' && *psz != ' ') {
            psz++;
        }
    }
    *ppsz = psz;
    return(dwRet);
}



DWORD StringToOpts(
LPSTR psz)
{
    DWORD opts = 0;

    while (*psz != '\0' && *psz != ' ') {
        if (*psz >= 'a' && *psz <= 'z') {
            opts |= 1 << (*psz - 'a');
        } else if (*psz >= 'A' && *psz <= 'Z') {
            opts |= 1 << (*psz - 'A');
        } else {
            return(OPTS_ERROR);     // any non-letter option is an error.
        }
        psz++;
    }
    return(opts);
}


/*
 * Function to convert an option string to a DWORD of flags.  pszLegalArgs
 * is used to allow option validation at the same time.
 *
 * *ppszArgs is set to point to after the options on exit.
 * On error, returns OPTS_ERROR.
 */
DWORD GetOpts(
LPSTR *ppszArgs,
LPSTR pszLegalArgs) // OPTIONAL
{
    DWORD Opts = 0;
    LPSTR pszArgs = *ppszArgs;

    /*
     * Skip whitespace
     */
    while (*pszArgs == ' ') {
        pszArgs++;
    }
    /*
     * process '-' prepended options.
     */
    while (*pszArgs == '-') {
        pszArgs++;
        Opts = StringToOpts(pszArgs);
        /*
         * skip to whitespace or end.
         */
        while (*pszArgs != '\0' && *pszArgs != ' ') {
            pszArgs++;
        }
        /*
         * skip trailing whitespace.
         */
        while (*pszArgs == ' ') {
            pszArgs++;
        }
        *ppszArgs = pszArgs;

        /*
         * optionally validate against LegalArgs
         */
        if (pszLegalArgs != NULL && ((Opts & StringToOpts(pszLegalArgs)) != Opts)) {
            Opts = OPTS_ERROR;
            Print("Bad options.\n");
            return(Opts);
        }
    }
    return(Opts);
}



VOID PrintHuge(
LPSTR psz)
{
    /*
     * Looks like this is faulting these days - Print seems to be fixed
     * so I'm leaving this entry point for compatibility. (SAS)
     */
#ifdef ITWORKS
#define HUNK_SIZE   400
    int cch;
    CHAR chSave;

    /*
     * since dorky Print extension can't handle very long strings,
     * break it up into peices for it to chew.
     */
    cch = strlen(psz);
    while (cch > HUNK_SIZE) {
        chSave = psz[HUNK_SIZE];
        psz[HUNK_SIZE] = '\0';
        Print(psz);
        psz[HUNK_SIZE] = chSave;
        psz += HUNK_SIZE;
        cch -= HUNK_SIZE;
    }
#endif
    Print(psz);
}



/*
 * Dispatcher function used by generated entrypoint functions.
 */
VOID CommonEP(
PVOID pFunction,
LPSTR pszName,
int type,
LPSTR pszLegalOpts,
HANDLE hcp,
HANDLE hct,
DWORD dwcp,
#ifdef KERNEL
DWORD dwp,
#else // !KERNEL
PWINDBG_EXTENSION_APIS lpea,
#endif // !KERNEL
LPSTR lpas)
{
    BOOL dwOptions, fSuccess;
    PVOID param1, param2, param3;

    hCurrentProcess = hcp;
    hCurrentThread = hct;
    dwCurrentPc = dwcp;
#ifdef KERNEL
    dwProcessor = dwp;
    lpExtensionApis = &ExtensionApis;
#else // !KERNEL
    lpExtensionApis = lpea;
#endif // !KERNLE

#if 0
    DEBUGPRINT("CommonEP(%x, \"%s\", %d, \"%s\", %x, %x, %x, %x, \"%s\")\n",
            pFunction,
            pszName,
            type,
            pszLegalOpts,
            hcp,
            hct,
            dwcp,
#ifdef KERNEL
            dwp,
#else // !KERNLE
            lpea,
#endif // !KERNEL
            lpas);
#endif

    fCtrlCHit = FALSE;  // reset this with each command. (SAFEWHILE fix)
    switch (type) {
    case NOARGS:
        fSuccess = ((TYPE_NOARGS)pFunction)();
        goto Exit;
    }

    dwOptions = GetOpts(&lpas, pszLegalOpts);
    if (dwOptions == OPTS_ERROR) {
        fSuccess = Ihelp(0, pszName);
        goto Exit;
    }

    try {
        switch (type) {
        case CUSTOM:
            fSuccess = ((TYPE_CUSTOM)pFunction)(dwOptions, lpas);
            break;

        case STDARGS0:
            fSuccess = ((TYPE_STDARGS0)pFunction)(dwOptions);
            break;

        case STDARGS1:
            fSuccess = ((TYPE_STDARGS1)pFunction)(dwOptions, OptEvalExp(lpas));
            break;

        case STDARGS2:
            param1 = OptEvalExp2(&lpas);
            fSuccess = ((TYPE_STDARGS2)pFunction)(dwOptions, param1, OptEvalExp(lpas));
            break;

        case STDARGS3:
            param1 = OptEvalExp2(&lpas);
            param2 = OptEvalExp2(&lpas);
            fSuccess = ((TYPE_STDARGS3)pFunction)(dwOptions, param1, param2, OptEvalExp(lpas));
            break;

        case STDARGS4:
            param1 = OptEvalExp2(&lpas);
            param2 = OptEvalExp2(&lpas);
            param3 = OptEvalExp2(&lpas);
            fSuccess = ((TYPE_STDARGS4)pFunction)(dwOptions, param1, param2, param3, OptEvalExp(lpas));
            break;

        default:
            Print("CommonEP: Don't recognize function type %d.\n", type);
            break;
        }
    } except (GetExceptionCode() == STATUS_NONCONTINUABLE_EXCEPTION ?
                EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        Print(pszCantContinue, pszExtName);
    }

Exit:
    if (!fSuccess) {
        Print("%s failed.\n", pszName);
        Ihelp(0, pszName);
    }
}

/*
 * Entrypoint functions (generated from exts.h)
 */
#ifdef KERNEL
#define DOIT(name, h1, h2, opts, type)                  \
VOID name##(                                            \
    HANDLE hcp,                                         \
    HANDLE hct,                                         \
    DWORD dwcp,                                         \
    DWORD dwp,                                          \
    LPSTR lpas)                                         \
{                                                       \
    CommonEP(I##name, #name, type, opts, hcp, hct, dwcp, dwp, lpas); \
}
#else // !KERNEL
#define DOIT(name, h1, h2, opts, type)                  \
VOID name##(                                            \
    HANDLE hcp,                                         \
    HANDLE hct,                                         \
    DWORD dwcp,                                         \
    PWINDBG_EXTENSION_APIS lpea,                        \
    LPSTR lpas)                                         \
{                                                       \
    CommonEP(I##name, #name, type, opts, hcp, hct, dwcp, lpea, lpas); \
}
#endif // !KERNEL
#include "exts.h"
#undef DOIT


/*
 * Standard help extension - present in all standard extensions.
 */
BOOL Ihelp(
    DWORD opts,
    LPSTR lpas)
{
#define DOIT(name, help1, help2, opts, type)  { #name, help1, help2 },

    static struct {
        LPSTR pszCmdName;
        LPSTR pszHelp1;
        LPSTR pszHelp2;
    } he[] = {
#include "exts.h"
    };
#undef DOIT
    int i;

    while (*lpas == ' ')
        lpas++;

    if (*lpas == '\0') {
        Print("-------------- %s Debug Extension help:--------------\n\n", pszExtName);
        for (i = 0; i < sizeof(he) / sizeof(he[0]); i++) {
            if (IsCtrlCHit()) {
                break;
            }
            Print(he[i].pszHelp1);
            if (opts & OFLAG(v)) {
                PrintHuge(he[i].pszHelp2);
            }
        }
        return(TRUE);
    } else {
        for (i = 0; i < sizeof(he) / sizeof(he[0]); i++) {
            if (IsCtrlCHit()) {
                break;
            }
            if (strcmp(lpas, he[i].pszCmdName) == 0) {
                Print(he[i].pszHelp1);
                PrintHuge(he[i].pszHelp2);
                return(TRUE);
            }
        }
        Print("%s is not supported.\n", lpas);
    }

    return(FALSE);
}