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 static const char k_prompt_escape_char = '\1'; 24 25 Editline::Editline (const char *prog, // prog can't be NULL 26 const char *prompt, // can be NULL for no prompt 27 FILE *fin, 28 FILE *fout, 29 FILE *ferr) : 30 m_editline (NULL), 31 m_history (NULL), 32 m_history_event (), 33 m_program (), 34 m_prompt (), 35 m_lines_prompt (), 36 m_getc_buffer (), 37 m_getc_mutex (Mutex::eMutexTypeNormal), 38 m_getc_cond (), 39 // m_gets_mutex (Mutex::eMutexTypeNormal), 40 m_completion_callback (NULL), 41 m_completion_callback_baton (NULL), 42 m_line_complete_callback (NULL), 43 m_line_complete_callback_baton (NULL), 44 m_lines_command (Command::None), 45 m_lines_curr_line (0), 46 m_lines_max_line (0), 47 m_prompt_with_line_numbers (false), 48 m_getting_line (false), 49 m_got_eof (false), 50 m_interrupted (false) 51 { 52 if (prog && prog[0]) 53 { 54 m_program = prog; 55 m_editline = ::el_init(prog, fin, fout, ferr); 56 m_history = ::history_init(); 57 } 58 else 59 { 60 m_editline = ::el_init("lldb-tmp", fin, fout, ferr); 61 } 62 if (prompt && prompt[0]) 63 SetPrompt (prompt); 64 65 //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key 66 //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key 67 68 assert (m_editline); 69 ::el_set (m_editline, EL_CLIENTDATA, this); 70 ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char); 71 ::el_set (m_editline, EL_EDITOR, "emacs"); 72 if (m_history) 73 { 74 ::el_set (m_editline, EL_HIST, history, m_history); 75 } 76 ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete); 77 ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine); 78 ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine); 79 80 ::el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string 81 ::el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does. 82 ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key. 83 ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be autocompelte 84 85 // Source $PWD/.editrc then $HOME/.editrc 86 ::el_source (m_editline, NULL); 87 88 if (m_history) 89 { 90 ::history (m_history, &m_history_event, H_SETSIZE, 800); 91 ::history (m_history, &m_history_event, H_SETUNIQUE, 1); 92 } 93 94 // Always read through our callback function so we don't read 95 // stuff we aren't supposed to. This also stops the extra echoing 96 // that can happen when you have more input than editline can handle 97 // at once. 98 SetGetCharCallback(GetCharFromInputFileCallback); 99 100 LoadHistory(); 101 } 102 103 Editline::~Editline() 104 { 105 SaveHistory(); 106 107 if (m_history) 108 { 109 ::history_end (m_history); 110 m_history = NULL; 111 } 112 113 // Disable edit mode to stop the terminal from flushing all input 114 // during the call to el_end() since we expect to have multiple editline 115 // instances in this program. 116 ::el_set (m_editline, EL_EDITMODE, 0); 117 118 ::el_end(m_editline); 119 m_editline = NULL; 120 } 121 122 void 123 Editline::SetGetCharCallback (GetCharCallbackType callback) 124 { 125 ::el_set (m_editline, EL_GETCFN, callback); 126 } 127 128 FileSpec 129 Editline::GetHistoryFile() 130 { 131 char history_path[PATH_MAX]; 132 ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_program.c_str()); 133 return FileSpec(history_path, true); 134 } 135 136 bool 137 Editline::LoadHistory () 138 { 139 if (m_history) 140 { 141 FileSpec history_file(GetHistoryFile()); 142 if (history_file.Exists()) 143 ::history (m_history, &m_history_event, H_LOAD, history_file.GetPath().c_str()); 144 return true; 145 } 146 return false; 147 } 148 149 bool 150 Editline::SaveHistory () 151 { 152 if (m_history) 153 { 154 std::string history_path = GetHistoryFile().GetPath(); 155 ::history (m_history, &m_history_event, H_SAVE, history_path.c_str()); 156 return true; 157 } 158 return false; 159 } 160 161 162 Error 163 Editline::PrivateGetLine(std::string &line) 164 { 165 Error error; 166 if (m_interrupted) 167 { 168 error.SetErrorString("interrupted"); 169 return error; 170 } 171 172 line.clear(); 173 if (m_editline != NULL) 174 { 175 int line_len = 0; 176 const char *line_cstr = NULL; 177 // Call el_gets to prompt the user and read the user's input. 178 // { 179 // // Make sure we know when we are in el_gets() by using a mutex 180 // Mutex::Locker locker (m_gets_mutex); 181 line_cstr = ::el_gets (m_editline, &line_len); 182 // } 183 184 static int save_errno = (line_len < 0) ? errno : 0; 185 186 if (save_errno != 0) 187 { 188 error.SetError(save_errno, eErrorTypePOSIX); 189 } 190 else if (line_cstr) 191 { 192 // Decrement the length so we don't have newline characters in "line" for when 193 // we assign the cstr into the std::string 194 while (line_len > 0 && 195 (line_cstr[line_len - 1] == '\n' || 196 line_cstr[line_len - 1] == '\r')) 197 --line_len; 198 199 if (line_len > 0) 200 { 201 // We didn't strip the newlines, we just adjusted the length, and 202 // we want to add the history item with the newlines 203 if (m_history) 204 ::history (m_history, &m_history_event, H_ENTER, line_cstr); 205 206 // Copy the part of the c string that we want (removing the newline chars) 207 line.assign(line_cstr, line_len); 208 } 209 } 210 } 211 else 212 { 213 error.SetErrorString("the EditLine instance has been deleted"); 214 } 215 return error; 216 } 217 218 219 Error 220 Editline::GetLine(std::string &line) 221 { 222 Error error; 223 line.clear(); 224 225 // Set arrow key bindings for up and down arrows for single line 226 // mode where up and down arrows do prev/next history 227 ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow 228 ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow 229 m_interrupted = false; 230 231 if (!m_got_eof) 232 { 233 if (m_getting_line) 234 { 235 error.SetErrorString("already getting a line"); 236 return error; 237 } 238 if (m_lines_curr_line > 0) 239 { 240 error.SetErrorString("already getting lines"); 241 return error; 242 } 243 m_getting_line = true; 244 error = PrivateGetLine(line); 245 m_getting_line = false; 246 } 247 248 if (m_got_eof && line.empty()) 249 { 250 // Only set the error if we didn't get an error back from PrivateGetLine() 251 if (error.Success()) 252 error.SetErrorString("end of file"); 253 } 254 255 return error; 256 } 257 258 size_t 259 Editline::Push (const char *bytes, size_t len) 260 { 261 if (m_editline) 262 { 263 // Must NULL terminate the string for el_push() so we stick it 264 // into a std::string first 265 ::el_push(m_editline, std::string (bytes, len).c_str()); 266 return len; 267 } 268 return 0; 269 } 270 271 272 Error 273 Editline::GetLines(const std::string &end_line, StringList &lines) 274 { 275 Error error; 276 if (m_getting_line) 277 { 278 error.SetErrorString("already getting a line"); 279 return error; 280 } 281 if (m_lines_curr_line > 0) 282 { 283 error.SetErrorString("already getting lines"); 284 return error; 285 } 286 287 // Set arrow key bindings for up and down arrows for multiple line 288 // mode where up and down arrows do edit prev/next line 289 ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow 290 ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow 291 ::el_set (m_editline, EL_BIND, "^b", "ed-prev-history", NULL); 292 ::el_set (m_editline, EL_BIND, "^n", "ed-next-history", NULL); 293 m_interrupted = false; 294 295 LineStatus line_status = LineStatus::Success; 296 297 lines.Clear(); 298 299 FILE *out_file = GetOutputFile(); 300 FILE *err_file = GetErrorFile(); 301 m_lines_curr_line = 1; 302 while (line_status != LineStatus::Done) 303 { 304 const uint32_t line_idx = m_lines_curr_line-1; 305 if (line_idx >= lines.GetSize()) 306 lines.SetSize(m_lines_curr_line); 307 m_lines_max_line = lines.GetSize(); 308 m_lines_command = Command::None; 309 assert(line_idx < m_lines_max_line); 310 std::string &line = lines[line_idx]; 311 error = PrivateGetLine(line); 312 if (error.Fail()) 313 { 314 line_status = LineStatus::Error; 315 } 316 else 317 { 318 switch (m_lines_command) 319 { 320 case Command::None: 321 if (m_line_complete_callback) 322 { 323 line_status = m_line_complete_callback (this, 324 lines, 325 line_idx, 326 error, 327 m_line_complete_callback_baton); 328 } 329 else if (line == end_line) 330 { 331 line_status = LineStatus::Done; 332 } 333 334 if (line_status == LineStatus::Success) 335 { 336 ++m_lines_curr_line; 337 // If we already have content for the next line because 338 // we were editing previous lines, then populate the line 339 // with the appropriate contents 340 if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty()) 341 ::el_push (m_editline, lines[line_idx+1].c_str()); 342 } 343 else if (line_status == LineStatus::Error) 344 { 345 // Clear to end of line ("ESC[K"), then print the error, 346 // then go to the next line ("\n") and then move cursor up 347 // two lines ("ESC[2A"). 348 fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString()); 349 } 350 break; 351 case Command::EditPrevLine: 352 if (m_lines_curr_line > 1) 353 { 354 //::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 355 ::fprintf (out_file, "\033[1A\033[1000D\033[2K"); 356 if (!lines[line_idx-1].empty()) 357 ::el_push (m_editline, lines[line_idx-1].c_str()); 358 --m_lines_curr_line; 359 } 360 break; 361 case Command::EditNextLine: 362 // Allow the down arrow to create a new line 363 ++m_lines_curr_line; 364 //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); 365 ::fprintf (out_file, "\033[1B\033[1000D\033[2K"); 366 if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty()) 367 ::el_push (m_editline, lines[line_idx+1].c_str()); 368 break; 369 } 370 } 371 } 372 m_lines_curr_line = 0; 373 m_lines_command = Command::None; 374 375 // If we have a callback, call it one more time to let the 376 // user know the lines are complete 377 if (m_line_complete_callback) 378 m_line_complete_callback (this, 379 lines, 380 UINT32_MAX, 381 error, 382 m_line_complete_callback_baton); 383 384 return error; 385 } 386 387 unsigned char 388 Editline::HandleCompletion (int ch) 389 { 390 if (m_completion_callback == NULL) 391 return CC_ERROR; 392 393 const LineInfo *line_info = ::el_line(m_editline); 394 StringList completions; 395 int page_size = 40; 396 397 const int num_completions = m_completion_callback (line_info->buffer, 398 line_info->cursor, 399 line_info->lastchar, 400 0, // Don't skip any matches (start at match zero) 401 -1, // Get all the matches 402 completions, 403 m_completion_callback_baton); 404 405 FILE *out_file = GetOutputFile(); 406 407 // if (num_completions == -1) 408 // { 409 // ::el_insertstr (m_editline, m_completion_key); 410 // return CC_REDISPLAY; 411 // } 412 // else 413 if (num_completions == -2) 414 { 415 // Replace the entire line with the first string... 416 ::el_deletestr (m_editline, line_info->cursor - line_info->buffer); 417 ::el_insertstr (m_editline, completions.GetStringAtIndex(0)); 418 return CC_REDISPLAY; 419 } 420 421 // If we get a longer match display that first. 422 const char *completion_str = completions.GetStringAtIndex(0); 423 if (completion_str != NULL && *completion_str != '\0') 424 { 425 el_insertstr (m_editline, completion_str); 426 return CC_REDISPLAY; 427 } 428 429 if (num_completions > 1) 430 { 431 int num_elements = num_completions + 1; 432 ::fprintf (out_file, "\nAvailable completions:"); 433 if (num_completions < page_size) 434 { 435 for (int i = 1; i < num_elements; i++) 436 { 437 completion_str = completions.GetStringAtIndex(i); 438 ::fprintf (out_file, "\n\t%s", completion_str); 439 } 440 ::fprintf (out_file, "\n"); 441 } 442 else 443 { 444 int cur_pos = 1; 445 char reply; 446 int got_char; 447 while (cur_pos < num_elements) 448 { 449 int endpoint = cur_pos + page_size; 450 if (endpoint > num_elements) 451 endpoint = num_elements; 452 for (; cur_pos < endpoint; cur_pos++) 453 { 454 completion_str = completions.GetStringAtIndex(cur_pos); 455 ::fprintf (out_file, "\n\t%s", completion_str); 456 } 457 458 if (cur_pos >= num_elements) 459 { 460 ::fprintf (out_file, "\n"); 461 break; 462 } 463 464 ::fprintf (out_file, "\nMore (Y/n/a): "); 465 reply = 'n'; 466 got_char = el_getc(m_editline, &reply); 467 if (got_char == -1 || reply == 'n') 468 break; 469 if (reply == 'a') 470 page_size = num_elements - cur_pos; 471 } 472 } 473 474 } 475 476 if (num_completions == 0) 477 return CC_REFRESH_BEEP; 478 else 479 return CC_REDISPLAY; 480 } 481 482 Editline * 483 Editline::GetClientData (::EditLine *e) 484 { 485 Editline *editline = NULL; 486 if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0) 487 return editline; 488 return NULL; 489 } 490 491 FILE * 492 Editline::GetInputFile () 493 { 494 return GetFilePointer (m_editline, 0); 495 } 496 497 FILE * 498 Editline::GetOutputFile () 499 { 500 return GetFilePointer (m_editline, 1); 501 } 502 503 FILE * 504 Editline::GetErrorFile () 505 { 506 return GetFilePointer (m_editline, 2); 507 } 508 509 const char * 510 Editline::GetPrompt() 511 { 512 if (m_prompt_with_line_numbers && m_lines_curr_line > 0) 513 { 514 StreamString strm; 515 strm.Printf("%3u: ", m_lines_curr_line); 516 m_lines_prompt = std::move(strm.GetString()); 517 return m_lines_prompt.c_str(); 518 } 519 else 520 { 521 return m_prompt.c_str(); 522 } 523 } 524 525 void 526 Editline::SetPrompt (const char *p) 527 { 528 if (p && p[0]) 529 m_prompt = p; 530 else 531 m_prompt.clear(); 532 size_t start_pos = 0; 533 size_t escape_pos; 534 while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos) 535 { 536 m_prompt.insert(escape_pos, 1, k_prompt_escape_char); 537 start_pos += 2; 538 } 539 } 540 541 FILE * 542 Editline::GetFilePointer (::EditLine *e, int fd) 543 { 544 FILE *file_ptr = NULL; 545 if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0) 546 return file_ptr; 547 return NULL; 548 } 549 550 unsigned char 551 Editline::CallbackEditPrevLine (::EditLine *e, int ch) 552 { 553 Editline *editline = GetClientData (e); 554 if (editline->m_lines_curr_line > 1) 555 { 556 editline->m_lines_command = Command::EditPrevLine; 557 return CC_NEWLINE; 558 } 559 return CC_ERROR; 560 } 561 unsigned char 562 Editline::CallbackEditNextLine (::EditLine *e, int ch) 563 { 564 Editline *editline = GetClientData (e); 565 if (editline->m_lines_curr_line < editline->m_lines_max_line) 566 { 567 editline->m_lines_command = Command::EditNextLine; 568 return CC_NEWLINE; 569 } 570 return CC_ERROR; 571 } 572 573 unsigned char 574 Editline::CallbackComplete (::EditLine *e, int ch) 575 { 576 Editline *editline = GetClientData (e); 577 if (editline) 578 return editline->HandleCompletion (ch); 579 return CC_ERROR; 580 } 581 582 const char * 583 Editline::GetPromptCallback (::EditLine *e) 584 { 585 Editline *editline = GetClientData (e); 586 if (editline) 587 return editline->GetPrompt(); 588 return ""; 589 } 590 591 size_t 592 Editline::SetInputBuffer (const char *c, size_t len) 593 { 594 if (c && len > 0) 595 { 596 Mutex::Locker locker(m_getc_mutex); 597 SetGetCharCallback(GetCharInputBufferCallback); 598 m_getc_buffer.append(c, len); 599 m_getc_cond.Broadcast(); 600 } 601 return len; 602 } 603 604 int 605 Editline::GetChar (char *c) 606 { 607 Mutex::Locker locker(m_getc_mutex); 608 if (m_getc_buffer.empty()) 609 m_getc_cond.Wait(m_getc_mutex); 610 if (m_getc_buffer.empty()) 611 return 0; 612 *c = m_getc_buffer[0]; 613 m_getc_buffer.erase(0,1); 614 return 1; 615 } 616 617 int 618 Editline::GetCharInputBufferCallback (EditLine *e, char *c) 619 { 620 Editline *editline = GetClientData (e); 621 if (editline) 622 return editline->GetChar(c); 623 return 0; 624 } 625 626 int 627 Editline::GetCharFromInputFileCallback (EditLine *e, char *c) 628 { 629 Editline *editline = GetClientData (e); 630 if (editline && editline->m_got_eof == false) 631 { 632 char ch = ::fgetc(editline->GetInputFile()); 633 if (ch == '\x04' || ch == EOF) 634 { 635 editline->m_got_eof = true; 636 } 637 else 638 { 639 *c = ch; 640 return 1; 641 } 642 } 643 return 0; 644 } 645 646 void 647 Editline::Hide () 648 { 649 FILE *out_file = GetOutputFile(); 650 if (out_file) 651 { 652 const LineInfo *line_info = ::el_line(m_editline); 653 if (line_info) 654 ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer)); 655 } 656 } 657 658 659 void 660 Editline::Refresh() 661 { 662 ::el_set (m_editline, EL_REFRESH); 663 } 664 665 void 666 Editline::Interrupt () 667 { 668 m_interrupted = true; 669 if (m_getting_line || m_lines_curr_line > 0) 670 el_insertstr(m_editline, "\n"); // True to force the line to complete itself so we get exit from el_gets() 671 } 672