xref: /freebsd/contrib/llvm-project/lldb/source/Core/IOHandler.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- IOHandler.cpp -----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Core/IOHandler.h"
10 
11 #if defined(__APPLE__)
12 #include <deque>
13 #endif
14 #include <string>
15 
16 #include "lldb/Core/Debugger.h"
17 #include "lldb/Host/Config.h"
18 #include "lldb/Host/File.h"
19 #include "lldb/Host/StreamFile.h"
20 #include "lldb/Utility/AnsiTerminal.h"
21 #include "lldb/Utility/Predicate.h"
22 #include "lldb/Utility/Status.h"
23 #include "lldb/Utility/StreamString.h"
24 #include "lldb/Utility/StringList.h"
25 #include "lldb/lldb-forward.h"
26 
27 #if LLDB_ENABLE_LIBEDIT
28 #include "lldb/Host/Editline.h"
29 #endif
30 #include "lldb/Interpreter/CommandCompletions.h"
31 #include "lldb/Interpreter/CommandInterpreter.h"
32 #include "llvm/ADT/StringRef.h"
33 
34 #ifdef _WIN32
35 #include "lldb/Host/windows/windows.h"
36 #endif
37 
38 #include <memory>
39 #include <mutex>
40 #include <optional>
41 
42 #include <cassert>
43 #include <cctype>
44 #include <cerrno>
45 #include <clocale>
46 #include <cstdint>
47 #include <cstdio>
48 #include <cstring>
49 #include <type_traits>
50 
51 using namespace lldb;
52 using namespace lldb_private;
53 using llvm::StringRef;
54 
IOHandler(Debugger & debugger,IOHandler::Type type)55 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
56     : IOHandler(debugger, type,
57                 FileSP(),               // Adopt STDIN from top input reader
58                 LockableStreamFileSP(), // Adopt STDOUT from top input reader
59                 LockableStreamFileSP(), // Adopt STDERR from top input reader
60                 0                       // Flags
61 
62       ) {}
63 
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::LockableStreamFileSP & output_sp,const lldb::LockableStreamFileSP & error_sp,uint32_t flags)64 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
65                      const lldb::FileSP &input_sp,
66                      const lldb::LockableStreamFileSP &output_sp,
67                      const lldb::LockableStreamFileSP &error_sp, uint32_t flags)
68     : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
69       m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
70       m_user_data(nullptr), m_done(false), m_active(false) {
71   // If any files are not specified, then adopt them from the top input reader.
72   if (!m_input_sp || !m_output_sp || !m_error_sp)
73     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
74                                              m_error_sp);
75 }
76 
77 IOHandler::~IOHandler() = default;
78 
GetInputFD()79 int IOHandler::GetInputFD() {
80   return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
81 }
82 
GetOutputFD()83 int IOHandler::GetOutputFD() {
84   return (m_output_sp ? m_output_sp->GetUnlockedFile().GetDescriptor() : -1);
85 }
86 
GetErrorFD()87 int IOHandler::GetErrorFD() {
88   return (m_error_sp ? m_error_sp->GetUnlockedFile().GetDescriptor() : -1);
89 }
90 
GetInputFileSP()91 FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
92 
GetOutputStreamFileSP()93 LockableStreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
94 
GetErrorStreamFileSP()95 LockableStreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
96 
GetIsInteractive()97 bool IOHandler::GetIsInteractive() {
98   return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
99 }
100 
GetIsRealTerminal()101 bool IOHandler::GetIsRealTerminal() {
102   return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
103 }
104 
SetPopped(bool b)105 void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
106 
WaitForPop()107 void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
108 
PrintAsync(const char * s,size_t len,bool is_stdout)109 void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
110   lldb::LockableStreamFileSP stream_sp = is_stdout ? m_output_sp : m_error_sp;
111   LockedStreamFile locked_Stream = stream_sp->Lock();
112   locked_Stream.Write(s, len);
113 }
114 
PrintAsync(const char * s,size_t len,bool is_stdout)115 bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) {
116   std::lock_guard<std::recursive_mutex> guard(m_mutex);
117   if (!m_top)
118     return false;
119   m_top->PrintAsync(s, len, is_stdout);
120   return true;
121 }
122 
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)123 IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
124                                    bool default_response)
125     : IOHandlerEditline(
126           debugger, IOHandler::Type::Confirm,
127           nullptr, // nullptr editline_name means no history loaded/saved
128           llvm::StringRef(), // No prompt
129           llvm::StringRef(), // No continuation prompt
130           false,             // Multi-line
131           false, // Don't colorize the prompt (i.e. the confirm message.)
132           0, *this),
133       m_default_response(default_response), m_user_response(default_response) {
134   StreamString prompt_stream;
135   prompt_stream.PutCString(prompt);
136   if (m_default_response)
137     prompt_stream.Printf(": [Y/n] ");
138   else
139     prompt_stream.Printf(": [y/N] ");
140 
141   SetPrompt(prompt_stream.GetString());
142 }
143 
144 IOHandlerConfirm::~IOHandlerConfirm() = default;
145 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)146 void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
147                                          CompletionRequest &request) {
148   if (request.GetRawCursorPos() != 0)
149     return;
150   request.AddCompletion(m_default_response ? "y" : "n");
151 }
152 
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)153 void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
154                                               std::string &line) {
155   if (line.empty()) {
156     // User just hit enter, set the response to the default
157     m_user_response = m_default_response;
158     io_handler.SetIsDone(true);
159     return;
160   }
161 
162   if (line.size() == 1) {
163     switch (line[0]) {
164     case 'y':
165     case 'Y':
166       m_user_response = true;
167       io_handler.SetIsDone(true);
168       return;
169     case 'n':
170     case 'N':
171       m_user_response = false;
172       io_handler.SetIsDone(true);
173       return;
174     default:
175       break;
176     }
177   }
178 
179   if (line == "yes" || line == "YES" || line == "Yes") {
180     m_user_response = true;
181     io_handler.SetIsDone(true);
182   } else if (line == "no" || line == "NO" || line == "No") {
183     m_user_response = false;
184     io_handler.SetIsDone(true);
185   }
186 }
187 
188 std::optional<std::string>
IOHandlerSuggestion(IOHandler & io_handler,llvm::StringRef line)189 IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler,
190                                        llvm::StringRef line) {
191   return io_handler.GetDebugger()
192       .GetCommandInterpreter()
193       .GetAutoSuggestionForCommand(line);
194 }
195 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)196 void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
197                                           CompletionRequest &request) {
198   switch (m_completion) {
199   case Completion::None:
200     break;
201   case Completion::LLDBCommand:
202     io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
203     break;
204   case Completion::Expression:
205     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
206         io_handler.GetDebugger().GetCommandInterpreter(),
207         lldb::eVariablePathCompletion, request, nullptr);
208     break;
209   }
210 }
211 
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color,uint32_t line_number_start,IOHandlerDelegate & delegate)212 IOHandlerEditline::IOHandlerEditline(
213     Debugger &debugger, IOHandler::Type type,
214     const char *editline_name, // Used for saving history files
215     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
216     bool multi_line, bool color, uint32_t line_number_start,
217     IOHandlerDelegate &delegate)
218     : IOHandlerEditline(
219           debugger, type,
220           FileSP(),               // Inherit input from top input reader
221           LockableStreamFileSP(), // Inherit output from top input reader
222           LockableStreamFileSP(), // Inherit error from top input reader
223           0,                      // Flags
224           editline_name,          // Used for saving history files
225           prompt, continuation_prompt, multi_line, color, line_number_start,
226           delegate) {}
227 
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::LockableStreamFileSP & output_sp,const lldb::LockableStreamFileSP & error_sp,uint32_t flags,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color,uint32_t line_number_start,IOHandlerDelegate & delegate)228 IOHandlerEditline::IOHandlerEditline(
229     Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
230     const lldb::LockableStreamFileSP &output_sp,
231     const lldb::LockableStreamFileSP &error_sp, uint32_t flags,
232     const char *editline_name, // Used for saving history files
233     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
234     bool multi_line, bool color, uint32_t line_number_start,
235     IOHandlerDelegate &delegate)
236     : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
237 #if LLDB_ENABLE_LIBEDIT
238       m_editline_up(),
239 #endif
240       m_delegate(delegate), m_prompt(), m_continuation_prompt(),
241       m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
242       m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), m_color(color),
243       m_interrupt_exits(true) {
244   SetPrompt(prompt);
245 
246 #if LLDB_ENABLE_LIBEDIT
247   const bool use_editline = m_input_sp && m_output_sp && m_error_sp &&
248                             m_input_sp->GetIsRealTerminal();
249   if (use_editline) {
250     m_editline_up = std::make_unique<Editline>(
251         editline_name, m_input_sp ? m_input_sp->GetStream() : nullptr,
252         m_output_sp, m_error_sp, m_color);
253     m_editline_up->SetIsInputCompleteCallback(
254         [this](Editline *editline, StringList &lines) {
255           return this->IsInputCompleteCallback(editline, lines);
256         });
257 
258     m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) {
259       this->AutoCompleteCallback(request);
260     });
261     m_editline_up->SetRedrawCallback([this]() { this->RedrawCallback(); });
262 
263     if (debugger.GetUseAutosuggestion()) {
264       m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
265         return this->SuggestionCallback(line);
266       });
267       m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
268           debugger.GetAutosuggestionAnsiPrefix()));
269       m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
270           debugger.GetAutosuggestionAnsiSuffix()));
271     }
272     // See if the delegate supports fixing indentation
273     const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
274     if (indent_chars) {
275       // The delegate does support indentation, hook it up so when any
276       // indentation character is typed, the delegate gets a chance to fix it
277       FixIndentationCallbackType f = [this](Editline *editline,
278                                             const StringList &lines,
279                                             int cursor_position) {
280         return this->FixIndentationCallback(editline, lines, cursor_position);
281       };
282       m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars);
283     }
284   }
285 #endif
286   SetBaseLineNumber(m_base_line_number);
287   SetPrompt(prompt);
288   SetContinuationPrompt(continuation_prompt);
289 }
290 
~IOHandlerEditline()291 IOHandlerEditline::~IOHandlerEditline() {
292 #if LLDB_ENABLE_LIBEDIT
293   m_editline_up.reset();
294 #endif
295 }
296 
Activate()297 void IOHandlerEditline::Activate() {
298   IOHandler::Activate();
299   m_delegate.IOHandlerActivated(*this, GetIsInteractive());
300 }
301 
Deactivate()302 void IOHandlerEditline::Deactivate() {
303   IOHandler::Deactivate();
304   m_delegate.IOHandlerDeactivated(*this);
305 }
306 
TerminalSizeChanged()307 void IOHandlerEditline::TerminalSizeChanged() {
308 #if LLDB_ENABLE_LIBEDIT
309   if (m_editline_up)
310     m_editline_up->TerminalSizeChanged();
311 #endif
312 }
313 
314 // Split out a line from the buffer, if there is a full one to get.
SplitLine(std::string & line_buffer)315 static std::optional<std::string> SplitLine(std::string &line_buffer) {
316   size_t pos = line_buffer.find('\n');
317   if (pos == std::string::npos)
318     return std::nullopt;
319   std::string line =
320       std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r"));
321   line_buffer = line_buffer.substr(pos + 1);
322   return line;
323 }
324 
325 // If the final line of the file ends without a end-of-line, return
326 // it as a line anyway.
SplitLineEOF(std::string & line_buffer)327 static std::optional<std::string> SplitLineEOF(std::string &line_buffer) {
328   if (llvm::all_of(line_buffer, llvm::isSpace))
329     return std::nullopt;
330   std::string line = std::move(line_buffer);
331   line_buffer.clear();
332   return line;
333 }
334 
GetLine(std::string & line,bool & interrupted)335 bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
336 #if LLDB_ENABLE_LIBEDIT
337   if (m_editline_up) {
338     return m_editline_up->GetLine(line, interrupted);
339   }
340 #endif
341 
342   line.clear();
343 
344   if (GetIsInteractive()) {
345     const char *prompt = nullptr;
346 
347     if (m_multi_line && m_curr_line_idx > 0)
348       prompt = GetContinuationPrompt();
349 
350     if (prompt == nullptr)
351       prompt = GetPrompt();
352 
353     if (prompt && prompt[0]) {
354       if (m_output_sp) {
355         LockedStreamFile locked_stream = m_output_sp->Lock();
356         locked_stream.Printf("%s", prompt);
357       }
358     }
359   }
360 
361   std::optional<std::string> got_line = SplitLine(m_line_buffer);
362 
363   if (!got_line && !m_input_sp) {
364     // No more input file, we are done...
365     SetIsDone(true);
366     return false;
367   }
368 
369   FILE *in = m_input_sp ? m_input_sp->GetStream() : nullptr;
370   char buffer[256];
371 
372   if (!got_line && !in && m_input_sp) {
373     // there is no FILE*, fall back on just reading bytes from the stream.
374     while (!got_line) {
375       size_t bytes_read = sizeof(buffer);
376       Status error = m_input_sp->Read((void *)buffer, bytes_read);
377       if (error.Success() && !bytes_read) {
378         got_line = SplitLineEOF(m_line_buffer);
379         break;
380       }
381       if (error.Fail())
382         break;
383       m_line_buffer += StringRef(buffer, bytes_read);
384       got_line = SplitLine(m_line_buffer);
385     }
386   }
387 
388   if (!got_line && in) {
389     while (!got_line) {
390       char *r = fgets(buffer, sizeof(buffer), in);
391 #ifdef _WIN32
392       // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
393       // according to the docs on MSDN. However, this has evidently been a
394       // known bug since Windows 8. Therefore, we can't detect if a signal
395       // interrupted in the fgets. So pressing ctrl-c causes the repl to end
396       // and the process to exit. A temporary workaround is just to attempt to
397       // fgets twice until this bug is fixed.
398       if (r == nullptr)
399         r = fgets(buffer, sizeof(buffer), in);
400       // this is the equivalent of EINTR for Windows
401       if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
402         continue;
403 #endif
404       if (r == nullptr) {
405         if (ferror(in) && errno == EINTR)
406           continue;
407         if (feof(in))
408           got_line = SplitLineEOF(m_line_buffer);
409         break;
410       }
411       m_line_buffer += buffer;
412       got_line = SplitLine(m_line_buffer);
413     }
414   }
415 
416   if (got_line) {
417     line = *got_line;
418   }
419 
420   return (bool)got_line;
421 }
422 
423 #if LLDB_ENABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines)424 bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
425                                                 StringList &lines) {
426   return m_delegate.IOHandlerIsInputComplete(*this, lines);
427 }
428 
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position)429 int IOHandlerEditline::FixIndentationCallback(Editline *editline,
430                                               const StringList &lines,
431                                               int cursor_position) {
432   return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position);
433 }
434 
435 std::optional<std::string>
SuggestionCallback(llvm::StringRef line)436 IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
437   return m_delegate.IOHandlerSuggestion(*this, line);
438 }
439 
AutoCompleteCallback(CompletionRequest & request)440 void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
441   m_delegate.IOHandlerComplete(*this, request);
442 }
443 
RedrawCallback()444 void IOHandlerEditline::RedrawCallback() {
445   m_debugger.RedrawStatusline(/*update=*/false);
446 }
447 
448 #endif
449 
GetPrompt()450 const char *IOHandlerEditline::GetPrompt() {
451 #if LLDB_ENABLE_LIBEDIT
452   if (m_editline_up) {
453     return m_editline_up->GetPrompt();
454   } else {
455 #endif
456     if (m_prompt.empty())
457       return nullptr;
458 #if LLDB_ENABLE_LIBEDIT
459   }
460 #endif
461   return m_prompt.c_str();
462 }
463 
SetPrompt(llvm::StringRef prompt)464 bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
465   m_prompt = std::string(prompt);
466 
467 #if LLDB_ENABLE_LIBEDIT
468   if (m_editline_up) {
469     m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
470     m_editline_up->SetPromptAnsiPrefix(
471         ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
472     m_editline_up->SetPromptAnsiSuffix(
473         ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
474   }
475 #endif
476   return true;
477 }
478 
SetUseColor(bool use_color)479 bool IOHandlerEditline::SetUseColor(bool use_color) {
480   m_color = use_color;
481 
482 #if LLDB_ENABLE_LIBEDIT
483   if (m_editline_up) {
484     m_editline_up->UseColor(use_color);
485     m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
486         m_debugger.GetAutosuggestionAnsiPrefix()));
487     m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
488         m_debugger.GetAutosuggestionAnsiSuffix()));
489   }
490 #endif
491   return true;
492 }
493 
GetContinuationPrompt()494 const char *IOHandlerEditline::GetContinuationPrompt() {
495   return (m_continuation_prompt.empty() ? nullptr
496                                         : m_continuation_prompt.c_str());
497 }
498 
SetContinuationPrompt(llvm::StringRef prompt)499 void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
500   m_continuation_prompt = std::string(prompt);
501 
502 #if LLDB_ENABLE_LIBEDIT
503   if (m_editline_up)
504     m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
505                                              ? nullptr
506                                              : m_continuation_prompt.c_str());
507 #endif
508 }
509 
SetBaseLineNumber(uint32_t line)510 void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
511   m_base_line_number = line;
512 }
513 
GetCurrentLineIndex() const514 uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
515 #if LLDB_ENABLE_LIBEDIT
516   if (m_editline_up)
517     return m_editline_up->GetCurrentLine();
518 #endif
519   return m_curr_line_idx;
520 }
521 
GetCurrentLines() const522 StringList IOHandlerEditline::GetCurrentLines() const {
523 #if LLDB_ENABLE_LIBEDIT
524   if (m_editline_up)
525     return m_editline_up->GetInputAsStringList();
526 #endif
527   // When libedit is not used, the current lines can be gotten from
528   // `m_current_lines_ptr`, which is updated whenever a new line is processed.
529   // This doesn't happen when libedit is used, in which case
530   // `m_current_lines_ptr` is only updated when the full input is terminated.
531 
532   if (m_current_lines_ptr)
533     return *m_current_lines_ptr;
534   return StringList();
535 }
536 
GetLines(StringList & lines,bool & interrupted)537 bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
538   m_current_lines_ptr = &lines;
539 
540   bool success = false;
541 #if LLDB_ENABLE_LIBEDIT
542   if (m_editline_up) {
543     return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
544   } else {
545 #endif
546     bool done = false;
547     Status error;
548 
549     while (!done) {
550       // Show line numbers if we are asked to
551       std::string line;
552       if (m_base_line_number > 0 && GetIsInteractive()) {
553         if (m_output_sp) {
554           LockedStreamFile locked_stream = m_output_sp->Lock();
555           locked_stream.Printf("%u%s",
556                                m_base_line_number + (uint32_t)lines.GetSize(),
557                                GetPrompt() == nullptr ? " " : "");
558         }
559       }
560 
561       m_curr_line_idx = lines.GetSize();
562 
563       bool interrupted = false;
564       if (GetLine(line, interrupted) && !interrupted) {
565         lines.AppendString(line);
566         done = m_delegate.IOHandlerIsInputComplete(*this, lines);
567       } else {
568         done = true;
569       }
570     }
571     success = lines.GetSize() > 0;
572 #if LLDB_ENABLE_LIBEDIT
573   }
574 #endif
575   return success;
576 }
577 
578 // Each IOHandler gets to run until it is done. It should read data from the
579 // "in" and place output into "out" and "err and return when done.
Run()580 void IOHandlerEditline::Run() {
581   std::string line;
582   while (IsActive()) {
583     bool interrupted = false;
584     if (m_multi_line) {
585       StringList lines;
586       if (GetLines(lines, interrupted)) {
587         if (interrupted) {
588           m_done = m_interrupt_exits;
589           m_delegate.IOHandlerInputInterrupted(*this, line);
590 
591         } else {
592           line = lines.CopyList();
593           m_delegate.IOHandlerInputComplete(*this, line);
594         }
595       } else {
596         m_done = true;
597       }
598     } else {
599       if (GetLine(line, interrupted)) {
600         if (interrupted)
601           m_delegate.IOHandlerInputInterrupted(*this, line);
602         else
603           m_delegate.IOHandlerInputComplete(*this, line);
604       } else {
605         m_done = true;
606       }
607     }
608   }
609 }
610 
Cancel()611 void IOHandlerEditline::Cancel() {
612 #if LLDB_ENABLE_LIBEDIT
613   if (m_editline_up)
614     m_editline_up->Cancel();
615 #endif
616 }
617 
Interrupt()618 bool IOHandlerEditline::Interrupt() {
619   // Let the delgate handle it first
620   if (m_delegate.IOHandlerInterrupt(*this))
621     return true;
622 
623 #if LLDB_ENABLE_LIBEDIT
624   if (m_editline_up)
625     return m_editline_up->Interrupt();
626 #endif
627   return false;
628 }
629 
GotEOF()630 void IOHandlerEditline::GotEOF() {
631 #if LLDB_ENABLE_LIBEDIT
632   if (m_editline_up)
633     m_editline_up->Interrupt();
634 #endif
635 }
636 
PrintAsync(const char * s,size_t len,bool is_stdout)637 void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
638 #if LLDB_ENABLE_LIBEDIT
639   if (m_editline_up) {
640     lldb::LockableStreamFileSP stream_sp = is_stdout ? m_output_sp : m_error_sp;
641     m_editline_up->PrintAsync(stream_sp, s, len);
642   } else
643 #endif
644   {
645 #ifdef _WIN32
646     const char *prompt = GetPrompt();
647     if (prompt) {
648       // Back up over previous prompt using Windows API
649       CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
650       HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
651       GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
652       COORD coord = screen_buffer_info.dwCursorPosition;
653       coord.X -= strlen(prompt);
654       if (coord.X < 0)
655         coord.X = 0;
656       SetConsoleCursorPosition(console_handle, coord);
657     }
658 #endif
659     IOHandler::PrintAsync(s, len, is_stdout);
660 #ifdef _WIN32
661     if (prompt)
662       IOHandler::PrintAsync(prompt, strlen(prompt), is_stdout);
663 #endif
664   }
665 }
666 
Refresh()667 void IOHandlerEditline::Refresh() {
668 #if LLDB_ENABLE_LIBEDIT
669   if (m_editline_up)
670     m_editline_up->Refresh();
671 #endif
672 }
673