]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Host/common/Editline.cpp
MFV r266995:
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / source / Host / common / Editline.cpp
1 //===-- Editline.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
11 #include "lldb/Host/Editline.h"
12
13 #include "lldb/Core/Error.h"
14 #include "lldb/Core/StreamString.h"
15 #include "lldb/Core/StringList.h"
16 #include "lldb/Host/Host.h"
17
18 #include <limits.h>
19
20 using namespace lldb;
21 using namespace lldb_private;
22
23 namespace lldb_private {
24     typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
25     
26     
27     // EditlineHistory objects are sometimes shared between multiple
28     // Editline instances with the same program name. This class allows
29     // multiple editline instances to
30     //
31     
32     class EditlineHistory
33     {
34     private:
35         // Use static GetHistory() function to get a EditlineHistorySP to one of these objects
36         EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) :
37             m_history (NULL),
38             m_event (),
39             m_prefix (prefix),
40             m_path ()
41         {
42             m_history = ::history_init();
43             ::history (m_history, &m_event, H_SETSIZE, size);
44             if (unique_entries)
45                 ::history (m_history, &m_event, H_SETUNIQUE, 1);
46         }
47         
48         const char *
49         GetHistoryFilePath()
50         {
51             if (m_path.empty() && m_history && !m_prefix.empty())
52             {
53                 char history_path[PATH_MAX];
54                 ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_prefix.c_str());
55                 m_path = std::move(FileSpec(history_path, true).GetPath());
56             }
57             if (m_path.empty())
58                 return NULL;
59             return m_path.c_str();
60         }
61         
62     public:
63         
64         ~EditlineHistory()
65         {
66             Save ();
67             
68             if (m_history)
69             {
70                 ::history_end (m_history);
71                 m_history = NULL;
72             }
73         }
74
75         static EditlineHistorySP
76         GetHistory (const std::string &prefix)
77         {
78             typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
79             static Mutex g_mutex(Mutex::eMutexTypeRecursive);
80             static WeakHistoryMap g_weak_map;
81             Mutex::Locker locker (g_mutex);
82             WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix);
83             EditlineHistorySP history_sp;
84             if (pos != g_weak_map.end())
85             {
86                 history_sp = pos->second.lock();
87                 if (history_sp)
88                     return history_sp;
89                 g_weak_map.erase(pos);
90             }
91             history_sp.reset(new EditlineHistory(prefix, 800, true));
92             g_weak_map[prefix] = history_sp;
93             return history_sp;
94         }
95         
96         bool IsValid() const
97         {
98             return m_history != NULL;
99         }
100         
101         ::History *
102         GetHistoryPtr ()
103         {
104             return m_history;
105         }
106         
107         void
108         Enter (const char *line_cstr)
109         {
110             if (m_history)
111                 ::history (m_history, &m_event, H_ENTER, line_cstr);
112         }
113         
114         bool
115         Load ()
116         {
117             if (m_history)
118             {
119                 const char *path = GetHistoryFilePath();
120                 if (path)
121                 {
122                     ::history (m_history, &m_event, H_LOAD, path);
123                     return true;
124                 }
125             }
126             return false;
127         }
128         
129         bool
130         Save ()
131         {
132             if (m_history)
133             {
134                 const char *path = GetHistoryFilePath();
135                 if (path)
136                 {
137                     ::history (m_history, &m_event, H_SAVE, path);
138                     return true;
139                 }
140             }
141             return false;
142         }
143         
144     protected:
145         ::History *m_history;       // The history object
146         ::HistEvent m_event;// The history event needed to contain all history events
147         std::string m_prefix;       // The prefix name (usually the editline program name) to use when loading/saving history
148         std::string m_path;         // Path to the history file
149     };
150 }
151
152
153 static const char k_prompt_escape_char = '\1';
154
155 Editline::Editline (const char *prog,       // prog can't be NULL
156                     const char *prompt,     // can be NULL for no prompt
157                     bool configure_for_multiline,
158                     FILE *fin,
159                     FILE *fout,
160                     FILE *ferr) :
161     m_editline (NULL),
162     m_history_sp (),
163     m_prompt (),
164     m_lines_prompt (),
165     m_getting_char (false),
166     m_completion_callback (NULL),
167     m_completion_callback_baton (NULL),
168     m_line_complete_callback (NULL),
169     m_line_complete_callback_baton (NULL),
170     m_lines_command (Command::None),
171     m_line_offset (0),
172     m_lines_curr_line (0),
173     m_lines_max_line (0),
174     m_file (fileno(fin), false),
175     m_prompt_with_line_numbers (false),
176     m_getting_line (false),
177     m_got_eof (false),
178     m_interrupted (false)
179 {
180     if (prog && prog[0])
181     {
182         m_editline = ::el_init(prog, fin, fout, ferr);
183         
184         // Get a shared history instance
185         m_history_sp = EditlineHistory::GetHistory(prog);
186     }
187     else
188     {
189         m_editline = ::el_init("lldb-tmp", fin, fout, ferr);
190     }
191     
192     if (prompt && prompt[0])
193         SetPrompt (prompt);
194
195     //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key
196     //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key
197
198     assert (m_editline);
199     ::el_set (m_editline, EL_CLIENTDATA, this);
200
201     // only defined for newer versions of editline
202 #ifdef EL_PROMPT_ESC
203     ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char);
204 #else
205     // fall back on old prompt setting code
206     ::el_set (m_editline, EL_PROMPT, GetPromptCallback);
207 #endif
208     ::el_set (m_editline, EL_EDITOR, "emacs");
209     if (m_history_sp && m_history_sp->IsValid())
210     {
211         ::el_set (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
212     }
213     ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete);
214     // Keep old "lldb_complete" mapping for older clients that used this in their .editrc. editline also
215     // has a bad bug where if you have a bind command that tries to bind to a function name that doesn't
216     // exist, it will corrupt the heap and probably crash your process later.
217     ::el_set (m_editline, EL_ADDFN, "lldb_complete", "Editline completion function", Editline::CallbackComplete);
218     ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine);
219     ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine);
220
221     ::el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
222     ::el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does.
223     ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
224     ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be auto complete
225     
226     if (configure_for_multiline)
227     {
228         // Use escape sequences for control characters due to bugs in editline
229         // where "-k up" and "-k down" don't always work.
230         ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow
231         ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow
232         // Bindings for next/prev history
233         ::el_set (m_editline, EL_BIND, "^P", "ed-prev-history", NULL); // Map up arrow
234         ::el_set (m_editline, EL_BIND, "^N", "ed-next-history", NULL); // Map down arrow
235     }
236     else
237     {
238         // Use escape sequences for control characters due to bugs in editline
239         // where "-k up" and "-k down" don't always work.
240         ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow
241         ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow
242     }
243     
244     // Source $PWD/.editrc then $HOME/.editrc
245     ::el_source (m_editline, NULL);
246  
247     // Always read through our callback function so we don't read
248     // stuff we aren't supposed to. This also stops the extra echoing
249     // that can happen when you have more input than editline can handle
250     // at once.
251     SetGetCharCallback(GetCharFromInputFileCallback);
252
253     LoadHistory();
254 }
255
256 Editline::~Editline()
257 {
258     // EditlineHistory objects are sometimes shared between multiple
259     // Editline instances with the same program name. So just release
260     // our shared pointer and if we are the last owner, it will save the
261     // history to the history save file automatically.
262     m_history_sp.reset();
263     
264     // Disable edit mode to stop the terminal from flushing all input
265     // during the call to el_end() since we expect to have multiple editline
266     // instances in this program.
267     ::el_set (m_editline, EL_EDITMODE, 0);
268
269     ::el_end(m_editline);
270     m_editline = NULL;
271 }
272
273 void
274 Editline::SetGetCharCallback (GetCharCallbackType callback)
275 {
276     ::el_set (m_editline, EL_GETCFN, callback);
277 }
278
279 bool
280 Editline::LoadHistory ()
281 {
282     if (m_history_sp)
283         return m_history_sp->Load();
284     return false;
285 }
286
287 bool
288 Editline::SaveHistory ()
289 {
290     if (m_history_sp)
291         return m_history_sp->Save();
292     return false;
293 }
294
295
296 Error
297 Editline::PrivateGetLine(std::string &line)
298 {
299     Error error;
300     if (m_interrupted)
301     {
302         error.SetErrorString("interrupted");
303         return error;
304     }
305     
306     line.clear();
307     if (m_editline != NULL)
308     {
309         int line_len = 0;
310         // Call el_gets to prompt the user and read the user's input.
311         const char *line_cstr = ::el_gets (m_editline, &line_len);
312         
313         static int save_errno = (line_len < 0) ? errno : 0;
314         
315         if (save_errno != 0)
316         {
317             error.SetError(save_errno, eErrorTypePOSIX);
318         }
319         else if (line_cstr)
320         {
321             // Decrement the length so we don't have newline characters in "line" for when
322             // we assign the cstr into the std::string
323             llvm::StringRef line_ref (line_cstr);
324             line_ref = line_ref.rtrim("\n\r");
325             
326             if (!line_ref.empty() && !m_interrupted)
327             {
328                 // We didn't strip the newlines, we just adjusted the length, and
329                 // we want to add the history item with the newlines
330                 if (m_history_sp)
331                     m_history_sp->Enter(line_cstr);
332                 
333                 // Copy the part of the c string that we want (removing the newline chars)
334                 line = std::move(line_ref.str());
335             }
336         }
337     }
338     else
339     {
340         error.SetErrorString("the EditLine instance has been deleted");
341     }
342     return error;
343 }
344
345
346 Error
347 Editline::GetLine(std::string &line, bool &interrupted)
348 {
349     Error error;
350     interrupted = false;
351     line.clear();
352
353     // Set arrow key bindings for up and down arrows for single line
354     // mode where up and down arrows do prev/next history
355     m_interrupted = false;
356
357     if (!m_got_eof)
358     {
359         if (m_getting_line)
360         {
361             error.SetErrorString("already getting a line");
362             return error;
363         }
364         if (m_lines_curr_line > 0)
365         {
366             error.SetErrorString("already getting lines");
367             return error;
368         }
369         m_getting_line = true;
370         error = PrivateGetLine(line);
371         m_getting_line = false;
372     }
373
374     interrupted = m_interrupted;
375
376     if (m_got_eof && line.empty())
377     {
378         // Only set the error if we didn't get an error back from PrivateGetLine()
379         if (error.Success())
380             error.SetErrorString("end of file");
381     }
382
383     return error;
384 }
385
386 size_t
387 Editline::Push (const char *bytes, size_t len)
388 {
389     if (m_editline)
390     {
391         // Must NULL terminate the string for el_push() so we stick it
392         // into a std::string first
393         ::el_push(m_editline,
394                   const_cast<char*>(std::string (bytes, len).c_str()));
395         return len;
396     }
397     return 0;
398 }
399
400
401 Error
402 Editline::GetLines(const std::string &end_line, StringList &lines, bool &interrupted)
403 {
404     Error error;
405     interrupted = false;
406     if (m_getting_line)
407     {
408         error.SetErrorString("already getting a line");
409         return error;
410     }
411     if (m_lines_curr_line > 0)
412     {
413         error.SetErrorString("already getting lines");
414         return error;
415     }
416     
417     // Set arrow key bindings for up and down arrows for multiple line
418     // mode where up and down arrows do edit prev/next line
419     m_interrupted = false;
420
421     LineStatus line_status = LineStatus::Success;
422
423     lines.Clear();
424
425     FILE *out_file = GetOutputFile();
426     FILE *err_file = GetErrorFile();
427     m_lines_curr_line = 1;
428     while (line_status != LineStatus::Done)
429     {
430         const uint32_t line_idx = m_lines_curr_line-1;
431         if (line_idx >= lines.GetSize())
432             lines.SetSize(m_lines_curr_line);
433         m_lines_max_line = lines.GetSize();
434         m_lines_command = Command::None;
435         assert(line_idx < m_lines_max_line);
436         std::string &line = lines[line_idx];
437         error = PrivateGetLine(line);
438         if (error.Fail())
439         {
440             line_status = LineStatus::Error;
441         }
442         else if (m_interrupted)
443         {
444             interrupted = true;
445             line_status = LineStatus::Done;
446         }
447         else
448         {
449             switch (m_lines_command)
450             {
451                 case Command::None:
452                     if (m_line_complete_callback)
453                     {
454                         line_status = m_line_complete_callback (this,
455                                                                 lines,
456                                                                 line_idx,
457                                                                 error,
458                                                                 m_line_complete_callback_baton);
459                     }
460                     else if (line == end_line)
461                     {
462                         line_status = LineStatus::Done;
463                     }
464
465                     if (line_status == LineStatus::Success)
466                     {
467                         ++m_lines_curr_line;
468                         // If we already have content for the next line because
469                         // we were editing previous lines, then populate the line
470                         // with the appropriate contents
471                         if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
472                             ::el_push (m_editline,
473                                        const_cast<char*>(lines[line_idx+1].c_str()));
474                     }
475                     else if (line_status == LineStatus::Error)
476                     {
477                         // Clear to end of line ("ESC[K"), then print the error,
478                         // then go to the next line ("\n") and then move cursor up
479                         // two lines ("ESC[2A").
480                         fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString());
481                     }
482                     break;
483                 case Command::EditPrevLine:
484                     if (m_lines_curr_line > 1)
485                     {
486                         //::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line
487                         ::fprintf (out_file, "\033[1A\033[1000D\033[2K");
488                         if (!lines[line_idx-1].empty())
489                             ::el_push (m_editline,
490                                        const_cast<char*>(lines[line_idx-1].c_str()));
491                         --m_lines_curr_line;
492                     }
493                     break;
494                 case Command::EditNextLine:
495                     // Allow the down arrow to create a new line
496                     ++m_lines_curr_line;
497                     //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size()));
498                     ::fprintf (out_file, "\033[1B\033[1000D\033[2K");
499                     if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
500                         ::el_push (m_editline,
501                                    const_cast<char*>(lines[line_idx+1].c_str()));
502                     break;
503             }
504         }
505     }
506     m_lines_curr_line = 0;
507     m_lines_command = Command::None;
508
509     // If we have a callback, call it one more time to let the
510     // user know the lines are complete
511     if (m_line_complete_callback && !interrupted)
512         m_line_complete_callback (this,
513                                   lines,
514                                   UINT32_MAX,
515                                   error,
516                                   m_line_complete_callback_baton);
517
518     return error;
519 }
520
521 unsigned char
522 Editline::HandleCompletion (int ch)
523 {
524     if (m_completion_callback == NULL)
525         return CC_ERROR;
526
527     const LineInfo *line_info  = ::el_line(m_editline);
528     StringList completions;
529     int page_size = 40;
530         
531     const int num_completions = m_completion_callback (line_info->buffer,
532                                                        line_info->cursor,
533                                                        line_info->lastchar,
534                                                        0,     // Don't skip any matches (start at match zero)
535                                                        -1,    // Get all the matches
536                                                        completions,
537                                                        m_completion_callback_baton);
538     
539     FILE *out_file = GetOutputFile();
540
541 //    if (num_completions == -1)
542 //    {
543 //        ::el_insertstr (m_editline, m_completion_key);
544 //        return CC_REDISPLAY;
545 //    }
546 //    else
547     if (num_completions == -2)
548     {
549         // Replace the entire line with the first string...
550         ::el_deletestr (m_editline, line_info->cursor - line_info->buffer);
551         ::el_insertstr (m_editline, completions.GetStringAtIndex(0));
552         return CC_REDISPLAY;
553     }
554     
555     // If we get a longer match display that first.
556     const char *completion_str = completions.GetStringAtIndex(0);
557     if (completion_str != NULL && *completion_str != '\0')
558     {
559         el_insertstr (m_editline, completion_str);
560         return CC_REDISPLAY;
561     }
562     
563     if (num_completions > 1)
564     {
565         int num_elements = num_completions + 1;
566         ::fprintf (out_file, "\nAvailable completions:");
567         if (num_completions < page_size)
568         {
569             for (int i = 1; i < num_elements; i++)
570             {
571                 completion_str = completions.GetStringAtIndex(i);
572                 ::fprintf (out_file, "\n\t%s", completion_str);
573             }
574             ::fprintf (out_file, "\n");
575         }
576         else
577         {
578             int cur_pos = 1;
579             char reply;
580             int got_char;
581             while (cur_pos < num_elements)
582             {
583                 int endpoint = cur_pos + page_size;
584                 if (endpoint > num_elements)
585                     endpoint = num_elements;
586                 for (; cur_pos < endpoint; cur_pos++)
587                 {
588                     completion_str = completions.GetStringAtIndex(cur_pos);
589                     ::fprintf (out_file, "\n\t%s", completion_str);
590                 }
591                 
592                 if (cur_pos >= num_elements)
593                 {
594                     ::fprintf (out_file, "\n");
595                     break;
596                 }
597                 
598                 ::fprintf (out_file, "\nMore (Y/n/a): ");
599                 reply = 'n';
600                 got_char = el_getc(m_editline, &reply);
601                 if (got_char == -1 || reply == 'n')
602                     break;
603                 if (reply == 'a')
604                     page_size = num_elements - cur_pos;
605             }
606         }
607         
608     }
609     
610     if (num_completions == 0)
611         return CC_REFRESH_BEEP;
612     else
613         return CC_REDISPLAY;
614 }
615
616 Editline *
617 Editline::GetClientData (::EditLine *e)
618 {
619     Editline *editline = NULL;
620     if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0)
621         return editline;
622     return NULL;
623 }
624
625 FILE *
626 Editline::GetInputFile ()
627 {
628     return GetFilePointer (m_editline, 0);
629 }
630
631 FILE *
632 Editline::GetOutputFile ()
633 {
634     return GetFilePointer (m_editline, 1);
635 }
636
637 FILE *
638 Editline::GetErrorFile ()
639 {
640     return GetFilePointer (m_editline, 2);
641 }
642
643 const char *
644 Editline::GetPrompt()
645 {
646     if (m_prompt_with_line_numbers && m_lines_curr_line > 0)
647     {
648         StreamString strm;
649         strm.Printf("%3u: ", m_lines_curr_line);
650         m_lines_prompt = std::move(strm.GetString());
651         return m_lines_prompt.c_str();
652     }
653     else
654     {
655         return m_prompt.c_str();
656     }
657 }
658
659 void
660 Editline::SetPrompt (const char *p)
661 {
662     if (p && p[0])
663         m_prompt = p;
664     else
665         m_prompt.clear();
666     size_t start_pos = 0;
667     size_t escape_pos;
668     while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos)
669     {
670         m_prompt.insert(escape_pos, 1, k_prompt_escape_char);
671         start_pos += 2;
672     }
673 }
674
675 FILE *
676 Editline::GetFilePointer (::EditLine *e, int fd)
677 {
678     FILE *file_ptr = NULL;
679     if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0)
680         return file_ptr;
681     return NULL;
682 }
683
684 unsigned char
685 Editline::CallbackEditPrevLine (::EditLine *e, int ch)
686 {
687     Editline *editline = GetClientData (e);
688     if (editline->m_lines_curr_line > 1)
689     {
690         editline->m_lines_command = Command::EditPrevLine;
691         return CC_NEWLINE;
692     }
693     return CC_ERROR;
694 }
695 unsigned char
696 Editline::CallbackEditNextLine (::EditLine *e, int ch)
697 {
698     Editline *editline = GetClientData (e);
699     if (editline->m_lines_curr_line < editline->m_lines_max_line)
700     {
701         editline->m_lines_command = Command::EditNextLine;
702         return CC_NEWLINE;
703     }
704     return CC_ERROR;
705 }
706
707 unsigned char
708 Editline::CallbackComplete (::EditLine *e, int ch)
709 {
710     Editline *editline = GetClientData (e);
711     if (editline)
712         return editline->HandleCompletion (ch);
713     return CC_ERROR;
714 }
715
716 const char *
717 Editline::GetPromptCallback (::EditLine *e)
718 {
719     Editline *editline = GetClientData (e);
720     if (editline)
721         return editline->GetPrompt();
722     return "";
723 }
724
725 int
726 Editline::GetCharFromInputFileCallback (EditLine *e, char *c)
727 {
728     Editline *editline = GetClientData (e);
729     if (editline && editline->m_got_eof == false)
730     {
731         FILE *f = editline->GetInputFile();
732         if (f == NULL)
733         {
734             editline->m_got_eof = true;
735             return 0;
736         }
737         
738         
739         while (1)
740         {
741             lldb::ConnectionStatus status = eConnectionStatusSuccess;
742             char ch = 0;
743             // When we start to call el_gets() the editline library needs to
744             // output the prompt
745             editline->m_getting_char.SetValue(true, eBroadcastAlways);
746             const size_t n = editline->m_file.Read(&ch, 1, UINT32_MAX, status, NULL);
747             editline->m_getting_char.SetValue(false, eBroadcastAlways);
748             if (n)
749             {
750                 if (ch == '\x04')
751                 {
752                     // Only turn a CTRL+D into a EOF if we receive the
753                     // CTRL+D an empty line, otherwise it will forward
754                     // delete the character at the cursor
755                     const LineInfo *line_info = ::el_line(e);
756                     if (line_info != NULL &&
757                         line_info->buffer == line_info->cursor &&
758                         line_info->cursor == line_info->lastchar)
759                     {
760                         editline->m_got_eof = true;
761                         break;
762                     }
763                 }
764             
765                 if (status == eConnectionStatusEndOfFile)
766                 {
767                     editline->m_got_eof = true;
768                     break;
769                 }
770                 else
771                 {
772                     *c = ch;
773                     return 1;
774                 }
775             }
776             else
777             {
778                 switch (status)
779                 {
780                     case eConnectionStatusInterrupted:
781                         editline->m_interrupted = true;
782                         *c = '\n';
783                         return 1;
784
785                     case eConnectionStatusSuccess:         // Success
786                         break;
787                         
788                     case eConnectionStatusError:           // Check GetError() for details
789                     case eConnectionStatusTimedOut:        // Request timed out
790                     case eConnectionStatusEndOfFile:       // End-of-file encountered
791                     case eConnectionStatusNoConnection:    // No connection
792                     case eConnectionStatusLostConnection:  // Lost connection while connected to a valid connection
793                         editline->m_got_eof = true;
794                         break;
795                 }
796             }
797         }
798     }
799     return 0;
800 }
801
802 void
803 Editline::Hide ()
804 {
805     if (m_getting_line)
806     {
807         // If we are getting a line, we might have started to call el_gets() and
808         // it might be printing the prompt. Here we make sure we are actually getting
809         // a character. This way we know the entire prompt has been printed.
810         TimeValue timeout = TimeValue::Now();
811         timeout.OffsetWithSeconds(1);
812         if (m_getting_char.WaitForValueEqualTo(true, &timeout))
813         {
814             FILE *out_file = GetOutputFile();
815             if (out_file)
816             {
817                 const LineInfo *line_info  = ::el_line(m_editline);
818                 if (line_info)
819                     ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer));
820             }
821         }
822     }
823 }
824
825
826 void
827 Editline::Refresh()
828 {
829     if (m_getting_line)
830     {
831         // If we are getting a line, we might have started to call el_gets() and
832         // it might be printing the prompt. Here we make sure we are actually getting
833         // a character. This way we know the entire prompt has been printed.
834         TimeValue timeout = TimeValue::Now();
835         timeout.OffsetWithSeconds(1);
836         if (m_getting_char.WaitForValueEqualTo(true, &timeout))
837         {
838             ::el_set (m_editline, EL_REFRESH);
839         }
840     }
841 }
842
843 bool
844 Editline::Interrupt ()
845 {
846     m_interrupted = true;
847     if (m_getting_line || m_lines_curr_line > 0)
848         return m_file.InterruptRead();
849     return false; // Interrupt not handled as we weren't getting a line or lines
850 }