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