home *** CD-ROM | disk | FTP | other *** search
- /* ------------------------------------------------------------------------- *
- * debug\shell.cpp: generic shell routines
- * ------------------------------------------------------------------------- */
- #include "stdafx.h"
-
- #include <Winbase.h>
-
- #include "shell.h"
-
- /* ------------------------------------------------------------------------- *
- * ShellCommand routines
- * ------------------------------------------------------------------------- */
-
- ShellCommand::ShellCommand(const WCHAR *n, int min)
- {
- m_pName = n;
-
- if(min == 0)
- m_minMatchLength = wcslen(n);
- else
- m_minMatchLength = min;
- }
-
- void ShellCommand::Help(Shell *shell)
- {
- shell->Write(L"Usage: %s", m_pName);
- }
-
- /* ------------------------------------------------------------------------- *
- * ShellCommandTable routines
- * ------------------------------------------------------------------------- */
-
- struct ShellCommandEntry
- {
- HASHENTRY entry;
-
- ShellCommand *command;
- };
-
- class ShellCommandTable : CHashTableAndData<CNewData>
- {
- private:
- BOOL Cmp(const BYTE *pc1, const HASHENTRY *pc2)
- {
- return((WCHAR)pc1) != ((ShellCommandEntry*)pc2)->command->GetName()[0];
- };
-
- USHORT HASH(const WCHAR *name)
- {
- // Try and keep them alphabetic
- if(name[0] < L'a')
- return(name[0] - L'a');
- else
- return(name[0] - L'A');
- };
-
- BYTE *KEY(const WCHAR *name)
- {
- return ((BYTE *) name[0]);
- };
-
- bool Match(const WCHAR *string, size_t length, const WCHAR *match, size_t minMatchLength)
- {
- return(length >= minMatchLength && wcsncmp(string, match, length) == 0);
- }
-
- ShellCommandEntry *Find(const WCHAR *name)
- {
- return((ShellCommandEntry *) CHashTableAndData<CNewData>::Find(HASH(name), KEY(name)));
- }
-
- ShellCommandEntry *FindNext(ShellCommandEntry *cmdEntry)
- {
- USHORT index =
- CHashTableAndData<CNewData>::FindNext(KEY(cmdEntry->command->GetName()),
- ItemIndex(&cmdEntry->entry));
-
- if(index == 0xffff)
- return(NULL);
- else
- return((ShellCommandEntry *) EntryPtr(index));
- }
-
- public:
-
- ShellCommandTable() : CHashTableAndData<CNewData>(50)
- {
- NewInit(50, sizeof(ShellCommandEntry), 500);
- }
-
- bool AddCommand(ShellCommand *newCommand)
- {
- //
- // Check for duplicate entry
- //
- for(ShellCommandEntry *entry = Find(newCommand->GetName());
- entry != NULL;
- entry = FindNext(entry))
- {
- ShellCommand *command = entry->command;
-
- // If we find a match, the command may not be entered
- if(Match(newCommand->GetName(), newCommand->GetMinMatchLength(),
- command->GetName(), command->GetMinMatchLength()))
- return(false);
- }
-
- //
- // Add the new entry and fill out the data structure
- //
- ShellCommandEntry *newEntry = (ShellCommandEntry*) Add(HASH(newCommand->GetName()));
- _ASSERTE(newEntry != NULL);
- newEntry->command = newCommand;
-
- return(true);
- }
-
- ShellCommand *GetCommand(const WCHAR *string, size_t length)
- {
- for(ShellCommandEntry *entry = Find(string);
- entry != NULL;
- entry = FindNext(entry))
- {
- ShellCommand *command = entry->command;
-
- if(Match(string, length, command->GetName(), command->GetMinMatchLength()))
- return(command);
- }
-
- return(NULL);
- }
-
- ShellCommand *GetFirstCommand(HASHFIND *info)
- {
- ShellCommandEntry *entry = (ShellCommandEntry *) FindFirstEntry(info);
-
- if(entry == NULL)
- return(NULL);
- else
- return(entry->command);
- }
-
- ShellCommand *GetNextCommand(HASHFIND *info)
- {
- ShellCommandEntry *entry = (ShellCommandEntry *) FindNextEntry(info);
-
- if(entry == NULL)
- return(NULL);
- else
- return(entry->command);
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * Shell routines
- * ------------------------------------------------------------------------- */
-
- Shell::Shell()
- {
- m_pPrompt = L"(shell)";
- m_pCommands = new ShellCommandTable();
- _ASSERTE(m_pCommands != NULL);
- m_lastCommand[0] = L'\0';
- }
-
- Shell::~Shell()
- {
- HASHFIND info;
-
- for(ShellCommand *command = m_pCommands->GetFirstCommand(&info);
- command != NULL;
- command = m_pCommands->GetNextCommand(&info))
- {
- delete command;
- }
-
- delete m_pCommands;
- }
-
- void Shell::AddCommand(ShellCommand *command)
- {
- m_pCommands->AddCommand(command);
- }
-
- void Shell::PutCommand(FILE *f)
- {
- fwprintf(f, m_buffer);
- fwprintf(f, L"\n");
- }
-
- ShellCommand *Shell::GetCommand(const WCHAR *string, size_t length)
- {
- return(m_pCommands->GetCommand(string, length));
- }
-
- void Shell::ReadCommand()
- {
- Write(L"%s ", m_pPrompt);
-
- // !!! would be nice to handle some control characters -
- // we'll see what we get for free from stdio
-
- m_buffer[0] = L'\0';
-
- // @todo: ReadLine can fail if you hit ctrl-break while it in. It
- // retuns a failure, but we don't really want to quit at that
- // point. I'm leaving the old code for now just in case we find
- // the need to switch back.
- #if 0
- if(!ReadLine(m_buffer, BUFFER_SIZE))
- wcscpy(m_buffer, L"quit");
-
- DoCommand(m_buffer);
- #else
- if(ReadLine(m_buffer, BUFFER_SIZE))
- DoCommand(m_buffer);
- #endif
- }
-
- void Shell::DoCommand(const WCHAR *string)
- {
- const WCHAR *args;
- const WCHAR *commandName;
- ShellCommand *command;
-
- if(*string == L'\0')
- string = m_lastCommand;
- else
- wcscpy(m_lastCommand, string);
-
- args = string;
-
- if(GetStringArg(args, commandName))
- {
- command = m_pCommands->GetCommand(commandName, args - commandName);
- if(command == NULL)
- {
- //
- // I like to be able to hit return to clear some whitespace on the
- // screen :)
- //
- if(wcslen(string) != 0)
- Error(L"Unknown command \"%.*s\"\n", args - commandName, commandName);
- }
- else
- {
- while(*args && iswspace(*args))
- args++;
-
- command->Do(this, args);
- }
- }
- }
-
- #define MAX_SHELL_COMMANDS 256
-
- void Shell::Help()
- {
- HASHFIND info;
- ShellCommand *pCommand [MAX_SHELL_COMMANDS];
- int iNumCommands = 0;
-
- Write(L"\nThe following commands are available:\n\n");
-
- pCommand [iNumCommands] = m_pCommands->GetFirstCommand(&info);
-
- while ((pCommand [iNumCommands] != NULL) &&
- (iNumCommands < MAX_SHELL_COMMANDS))
- {
- iNumCommands++;
- pCommand [iNumCommands] = m_pCommands->GetNextCommand(&info);
- }
-
- // The commands that are returned are already sorted, it's just that
- // they don't start from "a". So, find the first entry starting with
- // "a" (there will be atleast one due to the "attach" command) and start
- // printing from there. Then wrap around.
-
- int iStartIndex = 0;
- bool fFound = false;
-
- while ((fFound == false) && (iStartIndex < iNumCommands))
- {
- WCHAR *strName = (WCHAR *)pCommand [iStartIndex] ->GetName();
- if ((strName[0] == L'a') || (strName[0] == L'A'))
- fFound = true;
- else
- iStartIndex++;
- }
-
- // assert that there's always one command starting with "a".
- _ASSERTE (iStartIndex < iNumCommands);
-
- // now print the commands starting from this one.
- int iEndMarker = iStartIndex;
- WCHAR buf[256];
-
- do
- {
- ShellCommand *command = pCommand [iStartIndex % iNumCommands];
- iStartIndex++;
-
- if (command->GetMinMatchLength() == 0)
- wcscpy(buf, command->GetName());
- else
- {
- wcsncpy(buf, command->GetName(), command->GetMinMatchLength());
- buf[command->GetMinMatchLength()] = L'\0';
-
- if (command->GetName()[command->GetMinMatchLength()] != 0)
- {
- wcscat(buf, L"[");
- wcscat(buf, command->GetName() +
- command->GetMinMatchLength());
- wcscat(buf, L"]");
- }
- }
-
- Write(L"%-20s%s\n", buf, command->ShortHelp(this));
-
- } while ((iStartIndex % iNumCommands) != iEndMarker);
- }
-
- void Shell::ReportError(long hr)
- {
- WCHAR* wszBuffer;
- WCHAR wsz[60];
-
- switch( hr )
- {
- case CORDBG_E_FUNCTION_NOT_IL:
- wszBuffer = L"The function has no managed body!\n";
- break;
-
- case CORDBG_E_UNRECOVERABLE_ERROR:
- wszBuffer = L"Unrecoverable error!\n";
- break;
-
- case CORDBG_E_PROCESS_TERMINATED:
- wszBuffer = L"The debugee has terminated!\n";
- break;
-
- default:
- {
- char *buffer;
-
- // Get the string error from the HR
- DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_ALLOCATE_BUFFER
- | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char *) &buffer, 0, NULL);
-
- if (!res)
- {
- _ASSERTE( FAILED( HRESULT_FROM_WIN32(GetLastError()) ));
- wsprintf(wsz, L"Non-Windows Error:hr:0x%x\n", hr);
- wszBuffer = wsz;
- break;
- }
-
- // Make the string Unicode and free the system-allocated string
- MAKE_WIDEPTR_FROMUTF8(bufferW, buffer);
- LocalFree(buffer);
-
- wszBuffer = bufferW;
- break;
- }
- }
-
- Error(wszBuffer);
- }
-
- void Shell::SystemError()
- {
- ReportError(GetLastError());
- }
-
- size_t Shell::GetArgArray(WCHAR *argString, const WCHAR **argArray, size_t argMax)
- {
- const WCHAR **pArg = argArray, **pArgEnd = pArg, **pArgMax = pArg + argMax;
-
- while(pArgEnd < pArgMax)
- {
- //
- // Skip leading white space
- //
- while(*argString && iswspace(*argString))
- argString++;
-
- if(*argString == 0)
- break;
-
- *pArgEnd++ = argString;
-
- //
- // Add some kind of quoting at some point
- //
-
- while(*argString && !iswspace(*argString))
- argString++;
-
- *argString++ = 0;
- }
-
- return(pArgEnd - argArray);
- }
-
- bool Shell::GetStringArg(const WCHAR *&string, const WCHAR *&result)
- {
- while(*string && iswspace(*string))
- string++;
-
- result = string;
-
- while(*string && !iswspace(*string))
- string++;
-
- return(true);
- }
-
- bool Shell::GetIntArg(const WCHAR *&string, int &result)
- {
- while(*string && iswspace(*string))
- string++;
-
- result = 0;
-
- if(string[0] == L'0' && towlower(string[1]) == L'x')
- {
- string += 2;
-
- while(iswxdigit(*string))
- {
- result <<= 4;
- if(iswdigit(*string))
- result += *string - L'0';
- else
- result += 10 + towlower(*string) - L'a';
-
- string++;
- }
-
- return(true);
- }
- else if(iswdigit(*string))
- {
- while(iswdigit(*string))
- {
- result *= 10;
- result += *string++ - L'0';
- }
-
- return(true);
- }
- else
- return(false);
- }
-
- bool Shell::GetInt64Arg(const WCHAR *&string, unsigned __int64 &result)
- {
- while(*string && iswspace(*string))
- string++;
-
- result = 0;
-
- if(string[0] == L'0' && towlower(string[1]) == L'x')
- {
- string += 2;
-
- while(iswxdigit(*string))
- {
- result <<= 4;
- if(iswdigit(*string))
- result += *string - L'0';
- else
- result += 10 + towlower(*string) - L'a';
-
- string++;
- }
-
- return(true);
- }
- else if(iswdigit(*string))
- {
- while(iswdigit(*string))
- {
- result *= 10;
- result += *string++ - L'0';
- }
-
- return(true);
- }
- else
- return(false);
- }
-
- /* ------------------------------------------------------------------------- *
- * HelpShellCommand routines
- * ------------------------------------------------------------------------- */
-
- void HelpShellCommand::Do(Shell *shell, const WCHAR *args)
- {
- if (*args == 0)
- Help(shell);
-
- const WCHAR *commandName;
-
- while ((shell->GetStringArg(args, commandName)) &&
- *commandName != 0)
- {
- ShellCommand *command = shell->GetCommand(commandName,
- args - commandName);
-
- if (command == NULL)
- shell->Error(L"Unknown command \"%.*s\"\n",
- args - commandName, commandName);
- else
- command->Help(shell);
-
- shell->Write(L"\n");
- }
- }
-
- void HelpShellCommand::Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <command>*\n");
- shell->Write(L"\tPrints help on the given commands.\n");
- shell->Help();
- }
-
- const WCHAR *HelpShellCommand::ShortHelp(Shell *shell)
- {
- return L"Get help";
- }
-
-
-