home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 40 / IOPROG_40.ISO / SOFT / NETFrameworkSDK.exe / comsdk.cab / samples1.exe / Debugger / shell.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  13.5 KB  |  539 lines

  1. /* ------------------------------------------------------------------------- *
  2.  * debug\shell.cpp: generic shell routines
  3.  * ------------------------------------------------------------------------- */
  4. #include "stdafx.h"
  5.  
  6. #include <Winbase.h>
  7.  
  8. #include "shell.h"
  9.  
  10. /* ------------------------------------------------------------------------- *
  11.  * ShellCommand routines
  12.  * ------------------------------------------------------------------------- */
  13.  
  14. ShellCommand::ShellCommand(const WCHAR *n, int min)
  15. {
  16.     m_pName = n; 
  17.  
  18.     if(min == 0)
  19.         m_minMatchLength = wcslen(n);
  20.     else
  21.         m_minMatchLength = min; 
  22. }
  23.  
  24. void ShellCommand::Help(Shell *shell)
  25. {
  26.     shell->Write(L"Usage: %s", m_pName);
  27. }
  28.  
  29. /* ------------------------------------------------------------------------- *
  30.  * ShellCommandTable routines
  31.  * ------------------------------------------------------------------------- */
  32.  
  33. struct ShellCommandEntry
  34. {
  35.     HASHENTRY entry;
  36.  
  37.     ShellCommand *command;
  38. };
  39.  
  40. class ShellCommandTable : CHashTableAndData<CNewData>
  41. {
  42. private:
  43.     BOOL Cmp(const BYTE *pc1, const HASHENTRY *pc2)
  44.     {
  45.         return((WCHAR)pc1) != ((ShellCommandEntry*)pc2)->command->GetName()[0];
  46.     };
  47.  
  48.     USHORT HASH(const WCHAR *name) 
  49.     {
  50.         // Try and keep them alphabetic
  51.         if(name[0] < L'a')
  52.             return(name[0] - L'a');
  53.         else
  54.             return(name[0] - L'A');
  55.     };
  56.  
  57.     BYTE *KEY(const WCHAR *name) 
  58.     {
  59.         return ((BYTE *) name[0]);
  60.     };
  61.  
  62.     bool Match(const WCHAR *string, size_t length, const WCHAR *match, size_t minMatchLength)
  63.     {
  64.         return(length >= minMatchLength && wcsncmp(string, match, length) == 0);
  65.     }
  66.  
  67.     ShellCommandEntry *Find(const WCHAR *name)
  68.     {
  69.         return((ShellCommandEntry *) CHashTableAndData<CNewData>::Find(HASH(name), KEY(name)));
  70.     }
  71.     
  72.     ShellCommandEntry *FindNext(ShellCommandEntry *cmdEntry)
  73.     {
  74.         USHORT index =
  75.             CHashTableAndData<CNewData>::FindNext(KEY(cmdEntry->command->GetName()),
  76.                                                   ItemIndex(&cmdEntry->entry));
  77.         
  78.         if(index == 0xffff)
  79.             return(NULL);
  80.         else
  81.             return((ShellCommandEntry *) EntryPtr(index));
  82.     }
  83.  
  84. public:
  85.  
  86.     ShellCommandTable() : CHashTableAndData<CNewData>(50) 
  87.     {
  88.         NewInit(50, sizeof(ShellCommandEntry), 500);
  89.     }
  90.  
  91.     bool AddCommand(ShellCommand *newCommand)
  92.     {
  93.         //
  94.         // Check for duplicate entry
  95.         //
  96.         for(ShellCommandEntry *entry = Find(newCommand->GetName());
  97.            entry != NULL;
  98.            entry = FindNext(entry))
  99.         {
  100.             ShellCommand *command = entry->command;
  101.  
  102.             // If we find a match, the command may not be entered
  103.             if(Match(newCommand->GetName(), newCommand->GetMinMatchLength(),
  104.                      command->GetName(), command->GetMinMatchLength()))
  105.                 return(false);
  106.         }
  107.  
  108.         //
  109.         // Add the new entry and fill out the data structure
  110.         //
  111.         ShellCommandEntry *newEntry = (ShellCommandEntry*) Add(HASH(newCommand->GetName()));
  112.         _ASSERTE(newEntry != NULL);
  113.         newEntry->command = newCommand;
  114.  
  115.         return(true);
  116.     }
  117.  
  118.     ShellCommand *GetCommand(const WCHAR *string, size_t length)
  119.     {
  120.         for(ShellCommandEntry *entry = Find(string);
  121.            entry != NULL;
  122.            entry = FindNext(entry))
  123.         {
  124.             ShellCommand *command = entry->command;
  125.  
  126.             if(Match(string, length, command->GetName(), command->GetMinMatchLength()))
  127.                 return(command);
  128.         }
  129.  
  130.         return(NULL);
  131.     }
  132.  
  133.     ShellCommand *GetFirstCommand(HASHFIND *info)
  134.     {
  135.         ShellCommandEntry *entry = (ShellCommandEntry *) FindFirstEntry(info);
  136.         
  137.         if(entry == NULL)
  138.             return(NULL);
  139.         else
  140.             return(entry->command);
  141.     }
  142.  
  143.     ShellCommand *GetNextCommand(HASHFIND *info)
  144.     {
  145.         ShellCommandEntry *entry = (ShellCommandEntry *) FindNextEntry(info);
  146.  
  147.         if(entry == NULL)
  148.             return(NULL);
  149.         else
  150.             return(entry->command);
  151.     }
  152. };
  153.  
  154. /* ------------------------------------------------------------------------- *
  155.  * Shell routines
  156.  * ------------------------------------------------------------------------- */
  157.  
  158. Shell::Shell()
  159. {
  160.     m_pPrompt = L"(shell)";
  161.     m_pCommands = new ShellCommandTable();
  162.     _ASSERTE(m_pCommands != NULL);
  163.     m_lastCommand[0] = L'\0';
  164. }
  165.  
  166. Shell::~Shell()
  167. {
  168.     HASHFIND info;
  169.  
  170.     for(ShellCommand *command = m_pCommands->GetFirstCommand(&info);
  171.        command != NULL;
  172.        command = m_pCommands->GetNextCommand(&info))
  173.     {
  174.         delete command;
  175.     }
  176.  
  177.     delete m_pCommands;
  178. }
  179.  
  180. void Shell::AddCommand(ShellCommand *command)
  181. {
  182.     m_pCommands->AddCommand(command); 
  183. }
  184.  
  185. void Shell::PutCommand(FILE *f)
  186. {
  187.     fwprintf(f, m_buffer);
  188.     fwprintf(f, L"\n");
  189. }
  190.  
  191. ShellCommand *Shell::GetCommand(const WCHAR *string, size_t length)
  192. {
  193.     return(m_pCommands->GetCommand(string, length));
  194.  
  195. void Shell::ReadCommand()
  196. {
  197.     Write(L"%s ", m_pPrompt);
  198.  
  199.     // !!! would be nice to handle some control characters - 
  200.     // we'll see what we get for free from stdio
  201.  
  202.     m_buffer[0] = L'\0';
  203.  
  204.     // @todo: ReadLine can fail if you hit ctrl-break while it in. It
  205.     // retuns a failure, but we don't really want to quit at that
  206.     // point. I'm leaving the old code for now just in case we find
  207.     // the need to switch back.
  208. #if 0
  209.     if(!ReadLine(m_buffer, BUFFER_SIZE))
  210.         wcscpy(m_buffer, L"quit");
  211.  
  212.     DoCommand(m_buffer);
  213. #else
  214.     if(ReadLine(m_buffer, BUFFER_SIZE))
  215.         DoCommand(m_buffer);
  216. #endif    
  217. }
  218.  
  219. void Shell::DoCommand(const WCHAR *string)
  220. {
  221.     const WCHAR     *args;
  222.     const WCHAR     *commandName;
  223.     ShellCommand    *command;
  224.  
  225.     if(*string == L'\0')
  226.         string = m_lastCommand;
  227.     else
  228.         wcscpy(m_lastCommand, string);
  229.  
  230.     args = string;
  231.  
  232.     if(GetStringArg(args, commandName))
  233.     {
  234.         command = m_pCommands->GetCommand(commandName, args - commandName);
  235.         if(command == NULL)
  236.         {
  237.             //
  238.             // I like to be able to hit return to clear some whitespace on the
  239.             // screen :)
  240.             //
  241.             if(wcslen(string) != 0)
  242.                 Error(L"Unknown command \"%.*s\"\n", args - commandName, commandName);
  243.         }
  244.         else
  245.         {
  246.             while(*args && iswspace(*args))
  247.                 args++;
  248.  
  249.             command->Do(this, args);
  250.         }
  251.     }
  252. }
  253.  
  254. #define MAX_SHELL_COMMANDS        256
  255.  
  256. void Shell::Help()
  257. {
  258.     HASHFIND        info;
  259.     ShellCommand *pCommand [MAX_SHELL_COMMANDS];
  260.     int iNumCommands = 0;
  261.  
  262.     Write(L"\nThe following commands are available:\n\n");
  263.  
  264.     pCommand [iNumCommands] = m_pCommands->GetFirstCommand(&info);
  265.     
  266.     while ((pCommand [iNumCommands] != NULL) && 
  267.             (iNumCommands < MAX_SHELL_COMMANDS))
  268.     {
  269.         iNumCommands++;
  270.         pCommand [iNumCommands] = m_pCommands->GetNextCommand(&info);
  271.     }
  272.  
  273.     // The commands that are returned are already sorted, it's just that 
  274.     // they don't start from "a". So, find the first entry starting with 
  275.     // "a" (there will be atleast one due to the "attach" command) and start
  276.     // printing from there. Then wrap around.
  277.  
  278.     int iStartIndex = 0;
  279.     bool fFound = false;
  280.  
  281.     while ((fFound == false) && (iStartIndex < iNumCommands))
  282.     {
  283.         WCHAR *strName = (WCHAR *)pCommand [iStartIndex] ->GetName();
  284.         if ((strName[0] == L'a') || (strName[0] == L'A'))
  285.             fFound = true;
  286.         else
  287.             iStartIndex++;
  288.     }
  289.  
  290.     // assert that there's always one command starting with "a".
  291.     _ASSERTE (iStartIndex < iNumCommands);
  292.  
  293.     // now print the commands starting from this one.
  294.     int iEndMarker = iStartIndex;
  295.     WCHAR buf[256];
  296.  
  297.     do
  298.     {
  299.         ShellCommand *command = pCommand [iStartIndex % iNumCommands];
  300.         iStartIndex++;
  301.  
  302.         if (command->GetMinMatchLength() == 0)
  303.             wcscpy(buf, command->GetName());
  304.         else
  305.         {
  306.             wcsncpy(buf, command->GetName(), command->GetMinMatchLength());
  307.             buf[command->GetMinMatchLength()] = L'\0';
  308.  
  309.             if (command->GetName()[command->GetMinMatchLength()] != 0)
  310.             {
  311.                 wcscat(buf, L"[");
  312.                 wcscat(buf, command->GetName() +
  313.                             command->GetMinMatchLength());
  314.                 wcscat(buf, L"]");
  315.             }
  316.         }
  317.  
  318.         Write(L"%-20s%s\n", buf, command->ShortHelp(this));
  319.         
  320.     } while ((iStartIndex % iNumCommands) != iEndMarker);
  321. }
  322.  
  323. void Shell::ReportError(long hr)
  324. {
  325.     WCHAR* wszBuffer;
  326.     WCHAR wsz[60];
  327.     
  328.     switch( hr )
  329.     {
  330.         case CORDBG_E_FUNCTION_NOT_IL:
  331.             wszBuffer = L"The function has no managed body!\n";
  332.             break;
  333.  
  334.         case CORDBG_E_UNRECOVERABLE_ERROR:
  335.             wszBuffer = L"Unrecoverable error!\n";
  336.             break;
  337.  
  338.         case CORDBG_E_PROCESS_TERMINATED:
  339.             wszBuffer = L"The debugee has terminated!\n";
  340.             break;
  341.         
  342.         default:
  343.         {
  344.             char *buffer;
  345.             
  346.             // Get the string error from the HR
  347.             DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM 
  348.                                        | FORMAT_MESSAGE_ALLOCATE_BUFFER
  349.                                        | FORMAT_MESSAGE_IGNORE_INSERTS, 
  350.                                        NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
  351.                                        (char *) &buffer, 0, NULL);
  352.  
  353.             if (!res)
  354.             {
  355.                 _ASSERTE( FAILED( HRESULT_FROM_WIN32(GetLastError()) ));
  356.                 wsprintf(wsz, L"Non-Windows Error:hr:0x%x\n", hr);
  357.                 wszBuffer = wsz;
  358.                 break;
  359.             }
  360.  
  361.             // Make the string Unicode and free the system-allocated string
  362.             MAKE_WIDEPTR_FROMUTF8(bufferW, buffer);
  363.             LocalFree(buffer);
  364.  
  365.             wszBuffer = bufferW;
  366.             break;
  367.         }
  368.     }
  369.     
  370.     Error(wszBuffer);
  371. }
  372.  
  373. void Shell::SystemError()
  374. {
  375.     ReportError(GetLastError());
  376. }
  377.  
  378. size_t Shell::GetArgArray(WCHAR *argString, const WCHAR **argArray, size_t argMax)
  379. {
  380.     const WCHAR **pArg = argArray, **pArgEnd = pArg, **pArgMax = pArg + argMax;
  381.  
  382.     while(pArgEnd < pArgMax)
  383.     {
  384.         // 
  385.         // Skip leading white space
  386.         //
  387.         while(*argString && iswspace(*argString))
  388.             argString++;
  389.  
  390.         if(*argString == 0)
  391.             break;
  392.  
  393.         *pArgEnd++ = argString;
  394.  
  395.         //
  396.         // Add some kind of quoting at some point
  397.         //
  398.  
  399.         while(*argString && !iswspace(*argString))
  400.             argString++;
  401.  
  402.         *argString++ = 0;
  403.     }
  404.  
  405.     return(pArgEnd - argArray);
  406. }
  407.  
  408. bool Shell::GetStringArg(const WCHAR *&string, const WCHAR *&result)
  409. {
  410.     while(*string && iswspace(*string))
  411.         string++;
  412.  
  413.     result = string;
  414.  
  415.     while(*string && !iswspace(*string))
  416.         string++;
  417.  
  418.     return(true);
  419. }
  420.  
  421. bool Shell::GetIntArg(const WCHAR *&string, int &result)
  422. {
  423.     while(*string && iswspace(*string))
  424.         string++;
  425.  
  426.     result = 0;
  427.  
  428.     if(string[0] == L'0' && towlower(string[1]) == L'x')
  429.     {
  430.         string += 2;
  431.  
  432.         while(iswxdigit(*string))
  433.         {
  434.             result <<= 4;
  435.             if(iswdigit(*string))
  436.                 result += *string - L'0';
  437.             else
  438.                 result += 10 + towlower(*string) - L'a';
  439.  
  440.             string++;
  441.         }
  442.  
  443.         return(true);
  444.     }
  445.     else if(iswdigit(*string))
  446.     {
  447.         while(iswdigit(*string))
  448.         {
  449.             result *= 10;
  450.             result += *string++ - L'0';
  451.         }
  452.  
  453.         return(true);
  454.     }
  455.     else
  456.         return(false);
  457. }
  458.  
  459. bool Shell::GetInt64Arg(const WCHAR *&string, unsigned __int64 &result)
  460. {
  461.     while(*string && iswspace(*string))
  462.         string++;
  463.  
  464.     result = 0;
  465.  
  466.     if(string[0] == L'0' && towlower(string[1]) == L'x')
  467.     {
  468.         string += 2;
  469.  
  470.         while(iswxdigit(*string))
  471.         {
  472.             result <<= 4;
  473.             if(iswdigit(*string))
  474.                 result += *string - L'0';
  475.             else
  476.                 result += 10 + towlower(*string) - L'a';
  477.  
  478.             string++;
  479.         }
  480.  
  481.         return(true);
  482.     }
  483.     else if(iswdigit(*string))
  484.     {
  485.         while(iswdigit(*string))
  486.         {
  487.             result *= 10;
  488.             result += *string++ - L'0';
  489.         }
  490.  
  491.         return(true);
  492.     }
  493.     else
  494.         return(false);
  495. }
  496.  
  497. /* ------------------------------------------------------------------------- *
  498.  * HelpShellCommand routines
  499.  * ------------------------------------------------------------------------- */
  500.  
  501. void HelpShellCommand::Do(Shell *shell, const WCHAR *args)
  502. {
  503.     if (*args == 0)
  504.         Help(shell);
  505.  
  506.     const WCHAR *commandName;
  507.  
  508.     while ((shell->GetStringArg(args, commandName)) &&
  509.            *commandName != 0)
  510.     {
  511.         ShellCommand *command = shell->GetCommand(commandName,
  512.                                                   args - commandName);
  513.  
  514.         if (command == NULL)
  515.             shell->Error(L"Unknown command \"%.*s\"\n",
  516.                          args - commandName, commandName);
  517.         else
  518.             command->Help(shell);
  519.  
  520.         shell->Write(L"\n");
  521.     }
  522. }
  523.  
  524. void HelpShellCommand::Help(Shell *shell)
  525. {
  526.     ShellCommand::Help(shell);
  527.     shell->Write(L" <command>*\n");
  528.     shell->Write(L"\tPrints help on the given commands.\n");
  529.     shell->Help();
  530. }
  531.  
  532. const WCHAR *HelpShellCommand::ShortHelp(Shell *shell)
  533. {
  534.     return L"Get help";
  535. }
  536.  
  537.  
  538.