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 
InputReader(Debugger & debugger)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 
~InputReader()36 InputReader::~InputReader ()
37 {
38 }
39 
40 Error
Initialize(Callback callback,void * baton,lldb::InputReaderGranularity granularity,const char * end_token,const char * prompt,bool echo)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
HandleRawBytes(const char * bytes,size_t bytes_len)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 *
GetPrompt() const297 InputReader::GetPrompt () const
298 {
299     if (!m_prompt.empty())
300         return m_prompt.c_str();
301     else
302         return NULL;
303 }
304 
305 void
RefreshPrompt()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
Notify(InputReaderAction notification)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
WaitOnReaderIsDone()355 InputReader::WaitOnReaderIsDone ()
356 {
357     m_reader_done.WaitForValueEqualTo (true);
358 }
359 
360 const char *
GranularityAsCString(lldb::InputReaderGranularity granularity)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
GetBatchMode()378 InputReader::HandlerData::GetBatchMode()
379 {
380     return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode();
381 }
382 
383 lldb::StreamSP
GetOutStream()384 InputReader::HandlerData::GetOutStream()
385 {
386     return reader.GetDebugger().GetAsyncOutputStream();
387 }
388