blob: bc1dc49b2fd2bf6087792478530511f54c2ea56d [file] [log] [blame]
Chris Lattner30fdc8d2010-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 Clayton66111032010-06-23 01:19:29 +000018InputReader::InputReader (Debugger &debugger) :
19 m_debugger (debugger),
Chris Lattner30fdc8d2010-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 Clayton66111032010-06-23 01:19:29 +0000130 if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0)
Chris Lattner30fdc8d2010-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 Clayton66111032010-06-23 01:19:29 +0000179 *this,
Chris Lattner30fdc8d2010-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;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000199 while (p < end)
200 {
201 const char ch = *p;
202 if (ch == '\n' || ch == '\r')
203 {
204 size_t line_length = p - line_start;
205 // Now skip the newline character
206 ++p;
207 // Skip a complete DOS newline if we run into one
208 if (ch == 0xd && p < end && *p == 0xa)
209 ++p;
210
211 if (line_start <= end_token && end_token < line_start + line_length)
212 {
213 SetIsDone(true);
214 m_callback (m_callback_baton,
Greg Clayton66111032010-06-23 01:19:29 +0000215 *this,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000216 eInputReaderGotToken,
217 line_start,
218 end_token - line_start);
219
220 return p - bytes;
221 }
222
223 size_t bytes_handled = m_callback (m_callback_baton,
Greg Clayton66111032010-06-23 01:19:29 +0000224 *this,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000225 eInputReaderGotToken,
226 line_start,
227 line_length);
228
229 end_line = p;
230
231 if (bytes_handled != line_length)
232 {
233 // The input reader wasn't able to handle all the data
234 return line_start - bytes + bytes_handled;
235 }
236
237
238 if (IsDone())
239 return p - bytes;
240
241 line_start = p;
242 }
243 else
244 {
245 ++p;
246 }
247 }
248
249 if (end_line)
250 return end_line - bytes;
251 }
252 break;
253
254
255 case eInputReaderGranularityAll:
256 {
257 // Nothing should be handle unless we see our end token
258 if (end_token)
259 {
260 size_t length = end_token - bytes;
261 size_t bytes_handled = m_callback (m_callback_baton,
Greg Clayton66111032010-06-23 01:19:29 +0000262 *this,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000263 eInputReaderGotToken,
264 bytes,
265 length);
266 m_done = true;
267
268 p += bytes_handled + m_end_token.size();
269
270 // Consume any white space, such as newlines, beyond the end token
271
272 while (p < end && isspace(*p))
273 ++p;
274
275 if (bytes_handled != length)
276 return bytes_handled;
277 else
278 {
279 return p - bytes;
280 //return bytes_handled + m_end_token.size();
281 }
282 }
283 return 0;
284 }
285 break;
286 }
287 return 0;
288}
289
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000290const char *
291InputReader::GetPrompt () const
292{
293 if (!m_prompt.empty())
294 return m_prompt.c_str();
295 else
296 return NULL;
297}
298
299void
300InputReader::RefreshPrompt ()
301{
302 if (!m_prompt.empty())
303 {
Greg Clayton66111032010-06-23 01:19:29 +0000304 FILE *out_fh = m_debugger.GetOutputFileHandle();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000305 if (out_fh)
306 ::fprintf (out_fh, "%s", m_prompt.c_str());
307 }
308}
309
310void
311InputReader::Notify (InputReaderAction notification)
312{
313 switch (notification)
314 {
315 case eInputReaderActivate:
316 case eInputReaderReactivate:
317 m_active = true;
318 break;
319
320 case eInputReaderDeactivate:
321 case eInputReaderDone:
322 m_active = false;
323 break;
324
325 case eInputReaderGotToken:
326 return; // We don't notify the tokens here, it is done in HandleRawBytes
327 }
328 if (m_callback)
Greg Clayton66111032010-06-23 01:19:29 +0000329 m_callback (m_callback_baton, *this, notification, NULL, 0);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000330}