blob: afda9fa25f618b2e268cbe6cf48dc15a496c6a92 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- 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 <string>
11
12#include "lldb/Core/InputReader.h"
13#include "lldb/Core/Debugger.h"
14
15using namespace lldb;
16using namespace lldb_private;
17
Greg Clayton63094e02010-06-23 01:19:29 +000018InputReader::InputReader (Debugger &debugger) :
19 m_debugger (debugger),
Chris Lattner24943d22010-06-08 16:52:24 +000020 m_callback (NULL),
21 m_callback_baton (NULL),
22 m_end_token (),
23 m_granularity (eInputReaderGranularityInvalid),
24 m_done (true),
25 m_echo (true),
26 m_active (false)
27{
28}
29
30InputReader::~InputReader ()
31{
32}
33
34Error
35InputReader::Initialize
36(
37 Callback callback,
38 void *baton,
39 lldb::InputReaderGranularity granularity,
40 const char *end_token,
41 const char *prompt,
42 bool echo
43)
44{
45 Error err;
46 m_callback = callback;
47 m_callback_baton = baton,
48 m_granularity = granularity;
49 if (end_token != NULL)
50 m_end_token = end_token;
51 if (prompt != NULL)
52 m_prompt = prompt;
53 m_done = true;
54 m_echo = echo;
55
56 if (m_granularity == eInputReaderGranularityInvalid)
57 {
58 err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'.");
59 }
60 else
61 if (end_token != NULL && granularity != eInputReaderGranularityInvalid)
62 {
63 if (granularity == eInputReaderGranularityByte)
64 {
65 // Check to see if end_token is longer than one byte.
66
67 if (strlen (end_token) > 1)
68 {
69 err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte).");
70 }
71 }
72 else if (granularity == eInputReaderGranularityWord)
73 {
74 // Check to see if m_end_token contains any white space (i.e. is multiple words).
75
76 const char *white_space = " \t\n";
77 size_t pos = m_end_token.find_first_of (white_space);
78 if (pos != std::string::npos)
79 {
80 err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word).");
81 }
82 }
83 else
84 {
85 // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens.
86
87 size_t pos = m_end_token.find_first_of ('\n');
88 if (pos != std::string::npos)
89 {
90 err.SetErrorString ("Invalid end token: End token cannot contain a newline.");
91 }
92 }
93 }
94
95 m_done = err.Fail();
96
97 return err;
98}
99
100size_t
101InputReader::HandleRawBytes (const char *bytes, size_t bytes_len)
102{
103 const char *end_token = NULL;
104
105 if (m_end_token.empty() == false)
106 {
107 end_token = ::strstr (bytes, m_end_token.c_str());
108 if (end_token >= bytes + bytes_len)
109 end_token = NULL;
110 }
111
112 const char *p = bytes;
113 const char *end = bytes + bytes_len;
114
115 switch (m_granularity)
116 {
117 case eInputReaderGranularityInvalid:
118 break;
119
120 case eInputReaderGranularityByte:
121 while (p < end)
122 {
123 if (end_token == p)
124 {
125 p += m_end_token.size();
126 SetIsDone(true);
127 break;
128 }
129
Greg Clayton63094e02010-06-23 01:19:29 +0000130 if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0)
Chris Lattner24943d22010-06-08 16:52:24 +0000131 break;
132 ++p;
133 if (IsDone())
134 break;
135 }
136 // Return how many bytes were handled.
137 return p - bytes;
138 break;
139
140
141 case eInputReaderGranularityWord:
142 {
143 char quote = '\0';
144 const char *word_start = NULL;
145 bool send_word = false;
146 for (; p < end; ++p, send_word = false)
147 {
148 if (end_token && end_token == p)
149 {
150 p += m_end_token.size();
151 SetIsDone(true);
152 break;
153 }
154
155 const char ch = *p;
156 if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\')))
157 {
158 // We have a space character or the terminating quote
159 send_word = word_start != NULL;
160 quote = '\0';
161 }
162 else if (quote)
163 {
164 // We are in the middle of a quoted character
165 continue;
166 }
167 else if (ch == '"' || ch == '\'' || ch == '`')
168 quote = ch;
169 else if (word_start == NULL)
170 {
171 // We have the first character in a word
172 word_start = p;
173 }
174
175 if (send_word)
176 {
177 const size_t word_len = p - word_start;
178 size_t bytes_handled = m_callback (m_callback_baton,
Greg Clayton63094e02010-06-23 01:19:29 +0000179 *this,
Chris Lattner24943d22010-06-08 16:52:24 +0000180 eInputReaderGotToken,
181 word_start,
182 word_len);
183
184 if (bytes_handled != word_len)
185 return word_start - bytes + bytes_handled;
186
187 if (IsDone())
188 return p - bytes;
189 }
190 }
191 }
192 break;
193
194
195 case eInputReaderGranularityLine:
196 {
197 const char *line_start = bytes;
198 const char *end_line = NULL;
199 const char *end = bytes + bytes_len;
200 while (p < end)
201 {
202 const char ch = *p;
203 if (ch == '\n' || ch == '\r')
204 {
205 size_t line_length = p - line_start;
206 // Now skip the newline character
207 ++p;
208 // Skip a complete DOS newline if we run into one
209 if (ch == 0xd && p < end && *p == 0xa)
210 ++p;
211
212 if (line_start <= end_token && end_token < line_start + line_length)
213 {
214 SetIsDone(true);
215 m_callback (m_callback_baton,
Greg Clayton63094e02010-06-23 01:19:29 +0000216 *this,
Chris Lattner24943d22010-06-08 16:52:24 +0000217 eInputReaderGotToken,
218 line_start,
219 end_token - line_start);
220
221 return p - bytes;
222 }
223
224 size_t bytes_handled = m_callback (m_callback_baton,
Greg Clayton63094e02010-06-23 01:19:29 +0000225 *this,
Chris Lattner24943d22010-06-08 16:52:24 +0000226 eInputReaderGotToken,
227 line_start,
228 line_length);
229
230 end_line = p;
231
232 if (bytes_handled != line_length)
233 {
234 // The input reader wasn't able to handle all the data
235 return line_start - bytes + bytes_handled;
236 }
237
238
239 if (IsDone())
240 return p - bytes;
241
242 line_start = p;
243 }
244 else
245 {
246 ++p;
247 }
248 }
249
250 if (end_line)
251 return end_line - bytes;
252 }
253 break;
254
255
256 case eInputReaderGranularityAll:
257 {
258 // Nothing should be handle unless we see our end token
259 if (end_token)
260 {
261 size_t length = end_token - bytes;
262 size_t bytes_handled = m_callback (m_callback_baton,
Greg Clayton63094e02010-06-23 01:19:29 +0000263 *this,
Chris Lattner24943d22010-06-08 16:52:24 +0000264 eInputReaderGotToken,
265 bytes,
266 length);
267 m_done = true;
268
269 p += bytes_handled + m_end_token.size();
270
271 // Consume any white space, such as newlines, beyond the end token
272
273 while (p < end && isspace(*p))
274 ++p;
275
276 if (bytes_handled != length)
277 return bytes_handled;
278 else
279 {
280 return p - bytes;
281 //return bytes_handled + m_end_token.size();
282 }
283 }
284 return 0;
285 }
286 break;
287 }
288 return 0;
289}
290
Chris Lattner24943d22010-06-08 16:52:24 +0000291const char *
292InputReader::GetPrompt () const
293{
294 if (!m_prompt.empty())
295 return m_prompt.c_str();
296 else
297 return NULL;
298}
299
300void
301InputReader::RefreshPrompt ()
302{
303 if (!m_prompt.empty())
304 {
Greg Clayton63094e02010-06-23 01:19:29 +0000305 FILE *out_fh = m_debugger.GetOutputFileHandle();
Chris Lattner24943d22010-06-08 16:52:24 +0000306 if (out_fh)
307 ::fprintf (out_fh, "%s", m_prompt.c_str());
308 }
309}
310
311void
312InputReader::Notify (InputReaderAction notification)
313{
314 switch (notification)
315 {
316 case eInputReaderActivate:
317 case eInputReaderReactivate:
318 m_active = true;
319 break;
320
321 case eInputReaderDeactivate:
322 case eInputReaderDone:
323 m_active = false;
324 break;
325
326 case eInputReaderGotToken:
327 return; // We don't notify the tokens here, it is done in HandleRawBytes
328 }
329 if (m_callback)
Greg Clayton63094e02010-06-23 01:19:29 +0000330 m_callback (m_callback_baton, *this, notification, NULL, 0);
Chris Lattner24943d22010-06-08 16:52:24 +0000331}