Home | History | Annotate | Download | only in Core
      1 //===-- InputReader.cpp -----------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "lldb/lldb-python.h"
     11 
     12 #include <string>
     13 
     14 #include "lldb/Core/InputReader.h"
     15 #include "lldb/Core/Debugger.h"
     16 #include "lldb/Interpreter/CommandInterpreter.h"
     17 
     18 using namespace lldb;
     19 using namespace lldb_private;
     20 
     21 InputReader::InputReader (Debugger &debugger) :
     22     m_debugger (debugger),
     23     m_callback (NULL),
     24     m_callback_baton (NULL),
     25     m_end_token (),
     26     m_granularity (eInputReaderGranularityInvalid),
     27     m_done (true),
     28     m_echo (true),
     29     m_active (false),
     30     m_reader_done (false),
     31     m_user_input(),
     32     m_save_user_input(false)
     33 {
     34 }
     35 
     36 InputReader::~InputReader ()
     37 {
     38 }
     39 
     40 Error
     41 InputReader::Initialize
     42 (
     43     Callback callback,
     44     void *baton,
     45     lldb::InputReaderGranularity granularity,
     46     const char *end_token,
     47     const char *prompt,
     48     bool echo
     49 )
     50 {
     51     Error err;
     52     m_callback = callback;
     53     m_callback_baton = baton,
     54     m_granularity = granularity;
     55     if (end_token != NULL)
     56         m_end_token = end_token;
     57     if (prompt != NULL)
     58         m_prompt = prompt;
     59     m_done = true;
     60     m_echo = echo;
     61 
     62     if (m_granularity == eInputReaderGranularityInvalid)
     63     {
     64         err.SetErrorString ("Invalid read token size:  Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'.");
     65     }
     66     else
     67     if (end_token != NULL && granularity != eInputReaderGranularityInvalid)
     68     {
     69         if (granularity == eInputReaderGranularityByte)
     70         {
     71             // Check to see if end_token is longer than one byte.
     72 
     73             if (strlen (end_token) > 1)
     74             {
     75                 err.SetErrorString ("Invalid end token:  End token cannot be larger than specified token size (byte).");
     76             }
     77         }
     78         else if (granularity == eInputReaderGranularityWord)
     79         {
     80             // Check to see if m_end_token contains any white space (i.e. is multiple words).
     81 
     82             const char *white_space = " \t\n";
     83             size_t pos = m_end_token.find_first_of (white_space);
     84             if (pos != std::string::npos)
     85             {
     86                 err.SetErrorString ("Invalid end token:  End token cannot be larger than specified token size (word).");
     87             }
     88         }
     89         else
     90         {
     91             // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens.
     92 
     93             size_t pos = m_end_token.find_first_of ('\n');
     94             if (pos != std::string::npos)
     95             {
     96                 err.SetErrorString ("Invalid end token:  End token cannot contain a newline.");
     97             }
     98         }
     99     }
    100 
    101     m_done = err.Fail();
    102 
    103     return err;
    104 }
    105 
    106 size_t
    107 InputReader::HandleRawBytes (const char *bytes, size_t bytes_len)
    108 {
    109     const char *end_token = NULL;
    110 
    111     if (m_end_token.empty() == false)
    112     {
    113         end_token = ::strstr (bytes, m_end_token.c_str());
    114         if (end_token >= bytes + bytes_len)
    115             end_token = NULL;
    116     }
    117 
    118     const char *p = bytes;
    119     const char *end = bytes + bytes_len;
    120 
    121     switch (m_granularity)
    122     {
    123     case eInputReaderGranularityInvalid:
    124         break;
    125 
    126     case eInputReaderGranularityByte:
    127         while (p < end)
    128         {
    129             if (end_token == p)
    130             {
    131                 p += m_end_token.size();
    132                 SetIsDone(true);
    133                 break;
    134             }
    135 
    136             if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0)
    137                 break;
    138             ++p;
    139             if (IsDone())
    140                 break;
    141         }
    142         // Return how many bytes were handled.
    143         return p - bytes;
    144         break;
    145 
    146 
    147     case eInputReaderGranularityWord:
    148         {
    149             char quote = '\0';
    150             const char *word_start = NULL;
    151             bool send_word = false;
    152             for (; p < end; ++p, send_word = false)
    153             {
    154                 if (end_token && end_token == p)
    155                 {
    156                     m_end_token.size();
    157                     SetIsDone(true);
    158                     break;
    159                 }
    160 
    161                 const char ch = *p;
    162                 if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\')))
    163                 {
    164                     // We have a space character or the terminating quote
    165                     send_word = word_start != NULL;
    166                     quote = '\0';
    167                 }
    168                 else if (quote)
    169                 {
    170                     // We are in the middle of a quoted character
    171                     continue;
    172                 }
    173                 else if (ch == '"' || ch == '\'' || ch == '`')
    174                     quote = ch;
    175                 else if (word_start == NULL)
    176                 {
    177                     // We have the first character in a word
    178                     word_start = p;
    179                 }
    180 
    181                 if (send_word)
    182                 {
    183                     const size_t word_len = p - word_start;
    184                     size_t bytes_handled = m_callback (m_callback_baton,
    185                                                        *this,
    186                                                        eInputReaderGotToken,
    187                                                        word_start,
    188                                                        word_len);
    189 
    190                     if (bytes_handled != word_len)
    191                         return word_start - bytes + bytes_handled;
    192 
    193                     if (IsDone())
    194                         return p - bytes;
    195                 }
    196             }
    197         }
    198         break;
    199 
    200 
    201     case eInputReaderGranularityLine:
    202         {
    203             const char *line_start = bytes;
    204             const char *end_line = NULL;
    205             while (p < end)
    206             {
    207                 const char ch = *p;
    208                 if (ch == '\n' || ch == '\r')
    209                 {
    210                     size_t line_length = p - line_start;
    211                     // Now skip the newline character
    212                     ++p;
    213                     // Skip a complete DOS newline if we run into one
    214                     if (ch == 0xd && p < end && *p == 0xa)
    215                         ++p;
    216 
    217                     if (line_start <= end_token && end_token < line_start + line_length)
    218                     {
    219                         SetIsDone(true);
    220                         m_callback (m_callback_baton,
    221                                     *this,
    222                                     eInputReaderGotToken,
    223                                     line_start,
    224                                     end_token - line_start);
    225 
    226                         return p - bytes;
    227                     }
    228 
    229                     size_t bytes_handled = m_callback (m_callback_baton,
    230                                                        *this,
    231                                                        eInputReaderGotToken,
    232                                                        line_start,
    233                                                        line_length);
    234 
    235                     end_line = p;
    236 
    237                     if (bytes_handled != line_length)
    238                     {
    239                         // The input reader wasn't able to handle all the data
    240                         return line_start - bytes + bytes_handled;
    241                     }
    242 
    243 
    244                     if (IsDone())
    245                         return p - bytes;
    246 
    247                     line_start = p;
    248                 }
    249                 else
    250                 {
    251                     ++p;
    252                 }
    253             }
    254 
    255             if (end_line)
    256                 return end_line - bytes;
    257         }
    258         break;
    259 
    260 
    261     case eInputReaderGranularityAll:
    262         {
    263             // Nothing should be handle unless we see our end token
    264             if (end_token)
    265             {
    266                 size_t length = end_token - bytes;
    267                 size_t bytes_handled = m_callback (m_callback_baton,
    268                                                    *this,
    269                                                    eInputReaderGotToken,
    270                                                    bytes,
    271                                                    length);
    272                 m_done = true;
    273 
    274                 p += bytes_handled + m_end_token.size();
    275 
    276                 // Consume any white space, such as newlines, beyond the end token
    277 
    278                 while (p < end && isspace(*p))
    279                     ++p;
    280 
    281                 if (bytes_handled != length)
    282                     return bytes_handled;
    283                 else
    284                 {
    285                     return p - bytes;
    286                     //return bytes_handled + m_end_token.size();
    287                 }
    288             }
    289             return 0;
    290         }
    291         break;
    292     }
    293     return 0;
    294 }
    295 
    296 const char *
    297 InputReader::GetPrompt () const
    298 {
    299     if (!m_prompt.empty())
    300         return m_prompt.c_str();
    301     else
    302         return NULL;
    303 }
    304 
    305 void
    306 InputReader::RefreshPrompt ()
    307 {
    308 	if (m_debugger.GetCommandInterpreter().GetBatchCommandMode())
    309         return;
    310 
    311     if (!m_prompt.empty())
    312     {
    313         File &out_file = m_debugger.GetOutputFile();
    314         if (out_file.IsValid())
    315         {
    316             out_file.Printf ("%s", m_prompt.c_str());
    317             out_file.Flush();
    318         }
    319     }
    320 }
    321 
    322 void
    323 InputReader::Notify (InputReaderAction notification)
    324 {
    325     switch (notification)
    326     {
    327     case eInputReaderActivate:
    328     case eInputReaderReactivate:
    329         m_active = true;
    330         m_reader_done.SetValue(false, eBroadcastAlways);
    331         break;
    332 
    333     case eInputReaderDeactivate:
    334     case eInputReaderDone:
    335         m_active = false;
    336         break;
    337 
    338     case eInputReaderAsynchronousOutputWritten:
    339         break;
    340 
    341     case eInputReaderInterrupt:
    342     case eInputReaderEndOfFile:
    343         break;
    344 
    345     case eInputReaderGotToken:
    346         return; // We don't notify the tokens here, it is done in HandleRawBytes
    347     }
    348     if (m_callback)
    349         m_callback (m_callback_baton, *this, notification, NULL, 0);
    350     if (notification == eInputReaderDone)
    351         m_reader_done.SetValue(true, eBroadcastAlways);
    352 }
    353 
    354 void
    355 InputReader::WaitOnReaderIsDone ()
    356 {
    357     m_reader_done.WaitForValueEqualTo (true);
    358 }
    359 
    360 const char *
    361 InputReader::GranularityAsCString (lldb::InputReaderGranularity granularity)
    362 {
    363     switch (granularity)
    364     {
    365     case eInputReaderGranularityInvalid:  return "invalid";
    366     case eInputReaderGranularityByte:     return "byte";
    367     case eInputReaderGranularityWord:     return "word";
    368     case eInputReaderGranularityLine:     return "line";
    369     case eInputReaderGranularityAll:      return "all";
    370     }
    371 
    372     static char unknown_state_string[64];
    373     snprintf(unknown_state_string, sizeof (unknown_state_string), "InputReaderGranularity = %i", granularity);
    374     return unknown_state_string;
    375 }
    376 
    377 bool
    378 InputReader::HandlerData::GetBatchMode()
    379 {
    380     return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode();
    381 }
    382 
    383 lldb::StreamSP
    384 InputReader::HandlerData::GetOutStream()
    385 {
    386     return reader.GetDebugger().GetAsyncOutputStream();
    387 }
    388