blob: c139a87387a6a8a27aa013b8eb2f3b400ddd8509 [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
18InputReader::InputReader () :
19 m_callback (NULL),
20 m_callback_baton (NULL),
21 m_end_token (),
22 m_granularity (eInputReaderGranularityInvalid),
23 m_done (true),
24 m_echo (true),
25 m_active (false)
26{
27}
28
29InputReader::~InputReader ()
30{
31}
32
33Error
34InputReader::Initialize
35(
36 Callback callback,
37 void *baton,
38 lldb::InputReaderGranularity granularity,
39 const char *end_token,
40 const char *prompt,
41 bool echo
42)
43{
44 Error err;
45 m_callback = callback;
46 m_callback_baton = baton,
47 m_granularity = granularity;
48 if (end_token != NULL)
49 m_end_token = end_token;
50 if (prompt != NULL)
51 m_prompt = prompt;
52 m_done = true;
53 m_echo = echo;
54
55 if (m_granularity == eInputReaderGranularityInvalid)
56 {
57 err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'.");
58 }
59 else
60 if (end_token != NULL && granularity != eInputReaderGranularityInvalid)
61 {
62 if (granularity == eInputReaderGranularityByte)
63 {
64 // Check to see if end_token is longer than one byte.
65
66 if (strlen (end_token) > 1)
67 {
68 err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte).");
69 }
70 }
71 else if (granularity == eInputReaderGranularityWord)
72 {
73 // Check to see if m_end_token contains any white space (i.e. is multiple words).
74
75 const char *white_space = " \t\n";
76 size_t pos = m_end_token.find_first_of (white_space);
77 if (pos != std::string::npos)
78 {
79 err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word).");
80 }
81 }
82 else
83 {
84 // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens.
85
86 size_t pos = m_end_token.find_first_of ('\n');
87 if (pos != std::string::npos)
88 {
89 err.SetErrorString ("Invalid end token: End token cannot contain a newline.");
90 }
91 }
92 }
93
94 m_done = err.Fail();
95
96 return err;
97}
98
99size_t
100InputReader::HandleRawBytes (const char *bytes, size_t bytes_len)
101{
102 const char *end_token = NULL;
103
104 if (m_end_token.empty() == false)
105 {
106 end_token = ::strstr (bytes, m_end_token.c_str());
107 if (end_token >= bytes + bytes_len)
108 end_token = NULL;
109 }
110
111 const char *p = bytes;
112 const char *end = bytes + bytes_len;
113
114 switch (m_granularity)
115 {
116 case eInputReaderGranularityInvalid:
117 break;
118
119 case eInputReaderGranularityByte:
120 while (p < end)
121 {
122 if (end_token == p)
123 {
124 p += m_end_token.size();
125 SetIsDone(true);
126 break;
127 }
128
129 if (m_callback (m_callback_baton, this, eInputReaderGotToken, p, 1) == 0)
130 break;
131 ++p;
132 if (IsDone())
133 break;
134 }
135 // Return how many bytes were handled.
136 return p - bytes;
137 break;
138
139
140 case eInputReaderGranularityWord:
141 {
142 char quote = '\0';
143 const char *word_start = NULL;
144 bool send_word = false;
145 for (; p < end; ++p, send_word = false)
146 {
147 if (end_token && end_token == p)
148 {
149 p += m_end_token.size();
150 SetIsDone(true);
151 break;
152 }
153
154 const char ch = *p;
155 if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\')))
156 {
157 // We have a space character or the terminating quote
158 send_word = word_start != NULL;
159 quote = '\0';
160 }
161 else if (quote)
162 {
163 // We are in the middle of a quoted character
164 continue;
165 }
166 else if (ch == '"' || ch == '\'' || ch == '`')
167 quote = ch;
168 else if (word_start == NULL)
169 {
170 // We have the first character in a word
171 word_start = p;
172 }
173
174 if (send_word)
175 {
176 const size_t word_len = p - word_start;
177 size_t bytes_handled = m_callback (m_callback_baton,
178 this,
179 eInputReaderGotToken,
180 word_start,
181 word_len);
182
183 if (bytes_handled != word_len)
184 return word_start - bytes + bytes_handled;
185
186 if (IsDone())
187 return p - bytes;
188 }
189 }
190 }
191 break;
192
193
194 case eInputReaderGranularityLine:
195 {
196 const char *line_start = bytes;
197 const char *end_line = NULL;
198 const char *end = bytes + bytes_len;
199 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,
215 this,
216 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,
224 this,
225 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,
262 this,
263 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
290
291FILE *
292InputReader::GetInputFileHandle ()
293{
294 return Debugger::GetSharedInstance().GetInputFileHandle ();
295}
296
297FILE *
298InputReader::GetOutputFileHandle ()
299{
300 return Debugger::GetSharedInstance().GetOutputFileHandle ();
301}
302
303const char *
304InputReader::GetPrompt () const
305{
306 if (!m_prompt.empty())
307 return m_prompt.c_str();
308 else
309 return NULL;
310}
311
312void
313InputReader::RefreshPrompt ()
314{
315 if (!m_prompt.empty())
316 {
317 FILE *out_fh = GetOutputFileHandle();
318 if (out_fh)
319 ::fprintf (out_fh, "%s", m_prompt.c_str());
320 }
321}
322
323void
324InputReader::Notify (InputReaderAction notification)
325{
326 switch (notification)
327 {
328 case eInputReaderActivate:
329 case eInputReaderReactivate:
330 m_active = true;
331 break;
332
333 case eInputReaderDeactivate:
334 case eInputReaderDone:
335 m_active = false;
336 break;
337
338 case eInputReaderGotToken:
339 return; // We don't notify the tokens here, it is done in HandleRawBytes
340 }
341 if (m_callback)
342 m_callback (m_callback_baton, this, notification, NULL, 0);
343}