Geant4 Cross Reference |
1 // 2 // ******************************************************************** 3 // * License and Disclaimer * 4 // * * 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. * 10 // * * 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitation of liability. * 17 // * * 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************************************** 25 // 26 // 27 // 28 29 #ifndef WIN32 30 31 # include "G4UItcsh.hh" 32 33 # include "G4StateManager.hh" 34 # include "G4Types.hh" 35 # include "G4UIcommandStatus.hh" 36 37 # include <cctype> 38 # include <cstdlib> 39 # include <fstream> 40 # include <sstream> 41 42 // ASCII character code 43 static const char AsciiCtrA = '\001'; 44 static const char AsciiCtrB = '\002'; 45 static const char AsciiCtrC = '\003'; 46 static const char AsciiCtrD = '\004'; 47 static const char AsciiCtrE = '\005'; 48 static const char AsciiCtrF = '\006'; 49 static const char AsciiCtrK = '\013'; 50 static const char AsciiCtrL = '\014'; 51 static const char AsciiCtrN = '\016'; 52 static const char AsciiCtrP = '\020'; 53 static const char AsciiCtrQ = '\021'; 54 static const char AsciiCtrS = '\023'; 55 static const char AsciiCtrZ = '\032'; 56 static const char AsciiTAB = '\011'; 57 static const char AsciiBS = '\010'; 58 static const char AsciiDEL = '\177'; 59 static const char AsciiESC = '\033'; 60 61 static const int AsciiPrintableMin = 32; 62 63 // history file 64 static const G4String historyFileName = "/.g4_hist"; 65 66 ///////////////////////////////////////////////////////// 67 G4UItcsh::G4UItcsh(const G4String& prompt, G4int maxhist) 68 : G4VUIshell(prompt), 69 commandLine(""), 70 cursorPosition(1), 71 commandHistory(maxhist), 72 maxHistory(maxhist), 73 currentHistoryNo(1), 74 relativeHistoryIndex(0) 75 ///////////////////////////////////////////////////////// 76 { 77 // get current terminal mode 78 tcgetattr(0, &tios); 79 80 // read a shell history file 81 const char* path = std::getenv("HOME"); 82 if (path == nullptr) return; 83 84 G4String homedir = path; 85 G4String fname = homedir + historyFileName; 86 87 std::ifstream histfile; 88 enum 89 { 90 BUFSIZE = 1024 91 }; 92 char linebuf[BUFSIZE]; 93 94 histfile.open(fname, std::ios::in); 95 while (histfile.good()) { 96 if (histfile.eof()) break; 97 98 histfile.getline(linebuf, BUFSIZE); 99 G4String aline = G4StrUtil::strip_copy(linebuf); 100 if (! aline.empty()) StoreHistory(linebuf); 101 } 102 histfile.close(); 103 } 104 105 ///////////////////// 106 G4UItcsh::~G4UItcsh() 107 ///////////////////// 108 { 109 // store a shell history 110 const char* path = std::getenv("HOME"); 111 if (path == nullptr) return; 112 113 G4String homedir = path; 114 G4String fname = homedir + historyFileName; 115 116 std::ofstream histfile; 117 histfile.open(fname, std::ios::out); 118 119 G4int n0hist = 1; 120 if (currentHistoryNo > maxHistory) n0hist = currentHistoryNo - maxHistory + 1; 121 122 for (G4int i = n0hist; i <= currentHistoryNo; i++) { 123 histfile << RestoreHistory(i) << G4endl; 124 } 125 126 histfile.close(); 127 } 128 129 ////////////////////////////////////////// 130 void G4UItcsh::MakePrompt(const char* msg) 131 ////////////////////////////////////////// 132 { 133 if (promptSetting.length() <= 1) { 134 promptString = promptSetting; 135 return; 136 } 137 138 promptString = ""; 139 G4int i; 140 for (i = 0; i < (G4int)promptSetting.length() - 1; ++i) { 141 if (promptSetting[i] == '%') { 142 switch (promptSetting[i + 1]) { 143 case 's': // current application status 144 { 145 G4String stateStr; 146 if (msg != nullptr) { 147 stateStr = msg; 148 } 149 else { 150 G4StateManager* statM = G4StateManager::GetStateManager(); 151 stateStr = statM->GetStateString(statM->GetCurrentState()); 152 } 153 promptString.append(stateStr); 154 i++; 155 } break; 156 case '/': // current working directory 157 promptString.append(currentCommandDir); 158 i++; 159 break; 160 case 'h': // history# 161 { 162 std::ostringstream os; 163 os << currentHistoryNo; 164 promptString.append(os.str()); 165 i++; 166 } break; 167 default: 168 break; 169 } 170 } 171 else { 172 promptString += promptSetting[i]; 173 } 174 } 175 176 // append last chaacter 177 if (i == G4int(promptSetting.length() - 1)) promptString += promptSetting[i]; 178 } 179 180 ////////////////////////////// 181 void G4UItcsh::ResetTerminal() 182 ////////////////////////////// 183 { 184 RestoreTerm(); 185 } 186 187 // -------------------------------------------------------------------- 188 // commad line operations 189 // -------------------------------------------------------------------- 190 ////////////////////////////////////// 191 void G4UItcsh::InitializeCommandLine() 192 ////////////////////////////////////// 193 { 194 commandLine = ""; 195 cursorPosition = 1; 196 } 197 198 /////////////////////////////////////// 199 void G4UItcsh::InsertCharacter(char cc) 200 /////////////////////////////////////// 201 { 202 if (cc < AsciiPrintableMin || (isprint(cc) == 0)) return; 203 204 // display... 205 G4cout << cc; 206 std::size_t i; 207 for (i = cursorPosition - 1; i < commandLine.length(); ++i) 208 G4cout << commandLine[(G4int)i]; 209 for (i = cursorPosition - 1; i < commandLine.length(); ++i) 210 G4cout << AsciiBS; 211 G4cout << std::flush; 212 213 // command line string... 214 if (IsCursorLast()) { // add 215 commandLine += cc; 216 } 217 else { // insert 218 commandLine.insert(cursorPosition - 1, G4String(1, cc)); 219 } 220 cursorPosition++; 221 } 222 223 /////////////////////////////////// 224 void G4UItcsh::BackspaceCharacter() 225 /////////////////////////////////// 226 { 227 if (cursorPosition == 1) return; 228 229 // display... 230 if (IsCursorLast()) { 231 G4cout << AsciiBS << ' ' << AsciiBS << std::flush; 232 } 233 else { 234 G4cout << AsciiBS; 235 std::size_t i; 236 for (i = cursorPosition - 2; i < commandLine.length() - 1; ++i) { 237 G4cout << commandLine[G4int(i + 1)]; 238 } 239 G4cout << ' '; 240 for (i = cursorPosition - 2; i < commandLine.length(); ++i) { 241 G4cout << AsciiBS; 242 } 243 G4cout << std::flush; 244 } 245 246 // command line string... 247 commandLine.erase(cursorPosition - 2, 1); 248 249 cursorPosition--; 250 } 251 252 //////////////////////////////// 253 void G4UItcsh::DeleteCharacter() 254 //////////////////////////////// 255 { 256 if (IsCursorLast()) return; 257 258 // display... 259 std::size_t i; 260 for (i = cursorPosition - 1; i < commandLine.length() - 1; ++i) { 261 G4cout << commandLine[G4int(i + 1)]; 262 } 263 G4cout << ' '; 264 for (i = cursorPosition - 1; i < commandLine.length(); ++i) { 265 G4cout << AsciiBS; 266 } 267 G4cout << std::flush; 268 269 // command lin string... 270 commandLine.erase(cursorPosition - 1, 1); 271 } 272 273 ////////////////////////// 274 void G4UItcsh::ClearLine() 275 ////////////////////////// 276 { 277 // display... 278 std::size_t i; 279 for (i = cursorPosition; i >= 2; i--) 280 G4cout << AsciiBS; 281 for (i = 1; i <= commandLine.length(); ++i) 282 G4cout << ' '; 283 for (i = 1; i <= commandLine.length(); ++i) 284 G4cout << AsciiBS; 285 G4cout << std::flush; 286 287 // command line string... 288 commandLine.erase(); 289 cursorPosition = 1; 290 } 291 292 ///////////////////////////////// 293 void G4UItcsh::ClearAfterCursor() 294 ///////////////////////////////// 295 { 296 if (IsCursorLast()) return; 297 298 // display... 299 for (std::size_t i = cursorPosition; i <= commandLine.length(); ++i) 300 G4cout << ' '; 301 for (auto j = (G4int)commandLine.length(); j >= cursorPosition; --j) 302 G4cout << AsciiBS; 303 G4cout << std::flush; 304 305 // command line string... 306 commandLine.erase(cursorPosition - 1, commandLine.length() - cursorPosition + 1); 307 } 308 309 //////////////////////////// 310 void G4UItcsh::ClearScreen() 311 //////////////////////////// 312 { 313 if (! clearString.empty()) { 314 G4cout << clearString; 315 316 G4cout << promptString << commandLine << std::flush; 317 // reset cursur position 318 for (auto i = G4int(commandLine.length() + 1); i >= cursorPosition + 1; --i) 319 G4cout << AsciiBS << std::flush; 320 } 321 } 322 323 ////////////////////////////// 324 void G4UItcsh::ForwardCursor() 325 ////////////////////////////// 326 { 327 if (IsCursorLast()) return; 328 329 G4cout << commandLine[cursorPosition - 1] << std::flush; 330 cursorPosition++; 331 } 332 333 /////////////////////////////// 334 void G4UItcsh::BackwardCursor() 335 /////////////////////////////// 336 { 337 if (cursorPosition == 1) return; 338 339 cursorPosition--; 340 G4cout << AsciiBS << std::flush; 341 } 342 343 ////////////////////////////// 344 void G4UItcsh::MoveCursorTop() 345 ////////////////////////////// 346 { 347 for (G4int i = cursorPosition; i > 1; --i) { 348 G4cout << AsciiBS; 349 } 350 G4cout << std::flush; 351 cursorPosition = 1; 352 } 353 354 ////////////////////////////// 355 void G4UItcsh::MoveCursorEnd() 356 ////////////////////////////// 357 { 358 for (G4int i = cursorPosition - 1; i < (G4int)commandLine.length(); ++i) { 359 G4cout << commandLine[i]; 360 } 361 G4cout << std::flush; 362 cursorPosition = G4int(commandLine.length() + 1); 363 } 364 365 //////////////////////////////// 366 void G4UItcsh::PreviousCommand() 367 //////////////////////////////// 368 { 369 G4int nhmax = currentHistoryNo - 1 >= maxHistory ? maxHistory : currentHistoryNo - 1; 370 371 // retain current input 372 if (relativeHistoryIndex == 0) commandLineBuf = commandLine; 373 374 if (relativeHistoryIndex >= -nhmax + 1 && relativeHistoryIndex <= 0) { 375 ClearLine(); 376 relativeHistoryIndex--; 377 commandLine = RestoreHistory(currentHistoryNo + relativeHistoryIndex); 378 379 G4cout << commandLine << std::flush; 380 cursorPosition = G4int(commandLine.length() + 1); 381 } 382 } 383 384 //////////////////////////// 385 void G4UItcsh::NextCommand() 386 //////////////////////////// 387 { 388 G4int nhmax = currentHistoryNo - 1 >= maxHistory ? maxHistory : currentHistoryNo - 1; 389 390 if (relativeHistoryIndex >= -nhmax && relativeHistoryIndex <= -1) { 391 ClearLine(); 392 relativeHistoryIndex++; 393 394 if (relativeHistoryIndex == 0) 395 commandLine = commandLineBuf; 396 else 397 commandLine = RestoreHistory(currentHistoryNo + relativeHistoryIndex); 398 399 G4cout << commandLine << std::flush; 400 cursorPosition = G4int(commandLine.length() + 1); 401 } 402 } 403 404 /////////////////////////////////// 405 void G4UItcsh::ListMatchedCommand() 406 /////////////////////////////////// 407 { 408 G4cout << G4endl; 409 410 // input string 411 G4String input = G4StrUtil::lstrip_copy(commandLine); 412 // target token is last token 413 auto jhead = input.rfind(' '); 414 if (jhead != G4String::npos) { 415 input.erase(0, jhead); 416 G4StrUtil::lstrip(input); 417 } 418 419 // command tree of "user specified directory" 420 G4String vpath = currentCommandDir; 421 G4String vcmd = ""; 422 423 if (! input.empty()) { 424 auto len = (G4int)input.length(); 425 G4int indx = -1; 426 for (G4int i = len - 1; i >= 0; --i) { 427 if (input[i] == '/') { 428 indx = (G4int)i; 429 break; 430 } 431 } 432 // get abs. path 433 if (indx != -1) vpath = GetAbsCommandDirPath(input.substr(0, indx + 1)); 434 if (indx != 0 || len != 1) vcmd = input.substr(indx + 1, len - indx - 1); // care for "/" 435 } 436 437 // list matched dirs/commands 438 // G4cout << "@@@ vpath=" << vpath <<":vcmd=" << vcmd << G4endl; 439 ListCommand(vpath, vpath + vcmd); 440 441 G4cout << promptString << commandLine << std::flush; 442 } 443 444 //////////////////////////////// 445 void G4UItcsh::CompleteCommand() 446 //////////////////////////////// 447 { 448 // inputting string 449 G4String input = G4StrUtil::lstrip_copy(commandLine); 450 451 // target token is last token 452 auto jhead = input.rfind(' '); 453 if (jhead != G4String::npos) { 454 input.erase(0, jhead); 455 G4StrUtil::lstrip(input); 456 } 457 458 // tail string 459 std::size_t thead = input.find_last_of('/'); 460 G4String strtail = input; 461 if (thead != G4String::npos) strtail = input.substr(thead + 1, input.size() - thead - 1); 462 463 // command tree of "user specified directory" 464 G4String vpath = currentCommandDir; 465 G4String vcmd; 466 467 auto len = (G4int)input.length(); 468 if (! input.empty()) { 469 G4int indx = -1; 470 for (G4int i = len - 1; i >= 0; --i) { 471 if (input[i] == '/') { 472 indx = i; 473 break; 474 } 475 } 476 // get abs. path 477 if (indx != -1) vpath = GetAbsCommandDirPath(input.substr(0, indx + 1)); 478 if (indx != 0 || len != 1) vcmd = input.substr(indx + 1, len - indx - 1); // care for "/" 479 } 480 481 G4UIcommandTree* atree = GetCommandTree(vpath); // get command tree 482 if (atree == nullptr) return; 483 484 // list matched directories/commands 485 G4String stream, strtmp; 486 G4String inputpath = vpath + vcmd; 487 G4int nMatch = 0; 488 489 G4int Ndir = atree->GetTreeEntry(); 490 G4int Ncmd = atree->GetCommandEntry(); 491 492 // directory ... 493 for (G4int idir = 1; idir <= Ndir; idir++) { 494 G4String fpdir = atree->GetTree(idir)->GetPathName(); 495 // matching test 496 if (fpdir.find(inputpath, 0) == 0) { 497 if (nMatch == 0) { 498 stream = GetCommandPathTail(fpdir); 499 } 500 else { 501 strtmp = GetCommandPathTail(fpdir); 502 stream = GetFirstMatchedString(stream, strtmp); 503 } 504 nMatch++; 505 } 506 } 507 508 // command ... 509 for (G4int icmd = 1; icmd <= Ncmd; icmd++) { 510 G4String fpcmd = atree->GetPathName() + atree->GetCommand(icmd)->GetCommandName(); 511 // matching test 512 if (fpcmd.find(inputpath, 0) == 0) { 513 if (nMatch == 0) { 514 stream = GetCommandPathTail(fpcmd) + " "; 515 } 516 else { 517 strtmp = GetCommandPathTail(fpcmd) + " "; 518 stream = GetFirstMatchedString(stream, strtmp); 519 } 520 nMatch++; 521 } 522 } 523 524 // display... 525 input = commandLine; 526 // target token is last token 527 jhead = input.rfind(' '); 528 if (jhead == G4String::npos) 529 jhead = 0; 530 else 531 jhead++; 532 533 std::size_t jt = jhead; 534 535 G4String dspstr; 536 std::size_t i; 537 for (i = jt; i <= input.length() - 1; ++i) 538 dspstr += AsciiBS; 539 for (i = jt; i <= input.length() - 1; ++i) 540 dspstr += ' '; 541 for (i = jt; i <= input.length() - 1; ++i) 542 dspstr += AsciiBS; 543 544 dspstr += (vpath + stream); 545 if (nMatch == 0) dspstr += strtail; 546 G4cout << dspstr << std::flush; 547 548 // command line string 549 input.erase(jt); 550 input += (vpath + stream); 551 if (nMatch == 0) input += strtail; 552 553 commandLine = std::move(input); 554 cursorPosition = G4int(commandLine.length() + 1); 555 } 556 557 // -------------------------------------------------------------------- 558 // commad line 559 // -------------------------------------------------------------------- 560 ///////////////////////////// 561 G4String G4UItcsh::ReadLine() 562 ///////////////////////////// 563 { 564 InitializeCommandLine(); 565 566 char cc; 567 do { // input loop 568 G4cin.get(cc); 569 570 // treatment for special character 571 switch (cc) { 572 case AsciiCtrA: // ... move cursor to the top 573 MoveCursorTop(); 574 break; 575 case AsciiCtrB: // ... backward cursor 576 BackwardCursor(); 577 break; 578 case AsciiCtrD: // ... delete/exit/show matched list 579 if (commandLine.length() != 0 && IsCursorLast()) 580 ListMatchedCommand(); 581 else if (commandLine.empty()) { 582 return G4String("exit"); 583 } 584 else 585 DeleteCharacter(); 586 break; 587 case AsciiCtrE: // ... move cursor to the end 588 MoveCursorEnd(); 589 break; 590 case AsciiCtrF: // ... forward cursor 591 ForwardCursor(); 592 break; 593 case AsciiCtrK: // ... clear after the cursor 594 ClearAfterCursor(); 595 break; 596 case AsciiCtrL: // ... clear screen 597 // ClearScreen(); 598 break; 599 case AsciiCtrN: // ... next command 600 NextCommand(); 601 break; 602 case AsciiCtrP: // ... previous command 603 PreviousCommand(); 604 break; 605 case AsciiTAB: // ... command completion 606 if ((! commandLine.empty()) && IsCursorLast()) CompleteCommand(); 607 break; 608 case AsciiDEL: // ... backspace 609 BackspaceCharacter(); 610 break; 611 case AsciiBS: // ... backspace 612 BackspaceCharacter(); 613 break; 614 case AsciiCtrC: // ... kill prompt 615 break; 616 case AsciiCtrQ: // ... restarts suspeded output 617 break; 618 case AsciiCtrS: // ... suspend output 619 break; 620 case AsciiCtrZ: // ... suspend 621 break; 622 default: 623 break; 624 } 625 626 // treatment for ESC. character 627 if (cc == AsciiESC) { // ESC 628 G4cin.get(cc); 629 if (cc == '[' || cc == 'O') { // care for another termcap, such as konsole 630 G4cin.get(cc); 631 switch (cc) { 632 case 'A': // [UP] 633 cc = 'P' - '@'; 634 PreviousCommand(); // ... show previous commad 635 break; 636 case 'B': // [DOWN] 637 cc = 'N' - '@'; 638 NextCommand(); // ... show next commad 639 break; 640 case 'C': // [RIGHT] 641 cc = 'F' - '@'; 642 ForwardCursor(); // ... forward cursor 643 break; 644 case 'D': // [LEFT] 645 cc = 'B' - '@'; 646 BackwardCursor(); // ... backward cursor 647 break; 648 default: // who knows !? 649 cc = 0; 650 break; 651 } 652 } 653 } 654 655 // insert character to command line and display 656 InsertCharacter(cc); 657 658 } while (cc != '\n'); 659 660 return commandLine; 661 } 662 663 //////////////////////////////////////////////////////// 664 G4String G4UItcsh::GetCommandLineString(const char* msg) 665 //////////////////////////////////////////////////////// 666 { 667 SetTermToInputMode(); 668 669 MakePrompt(msg); // update 670 relativeHistoryIndex = 0; 671 672 G4cout << promptString << std::flush; 673 674 G4String newCommand = ReadLine(); // read line... 675 // multi-line 676 while ((newCommand.length() > 0) && (newCommand[G4int(newCommand.length() - 1)] == '_')) { 677 newCommand.erase(newCommand.length() - 1); 678 G4cout << G4endl; 679 promptString = "? "; 680 G4cout << promptString << std::flush; 681 G4String newLine = ReadLine(); 682 newCommand.append(newLine); 683 } 684 685 // update history... 686 G4bool isMeaningfull = false; // check NULL command 687 for (G4int i = 0; i < (G4int)newCommand.length(); ++i) { 688 if (newCommand[i] != ' ') { 689 isMeaningfull = true; 690 break; 691 } 692 } 693 if (! newCommand.empty() && isMeaningfull) StoreHistory(newCommand); 694 695 // reset terminal 696 RestoreTerm(); 697 698 G4cout << G4endl; 699 return newCommand; 700 } 701 702 //////////////////////////////////////////////////////////////////// 703 G4String G4UItcsh::GetFirstMatchedString(const G4String& str1, const G4String& str2) const 704 //////////////////////////////////////////////////////////////////// 705 { 706 std::size_t nlen1 = str1.length(); 707 std::size_t nlen2 = str2.length(); 708 709 std::size_t nmin = nlen1 < nlen2 ? nlen1 : nlen2; 710 711 G4String strMatched; 712 for (G4int i = 0; i < (G4int)nmin; ++i) { 713 if (str1[i] == str2[i]) { 714 strMatched += str1[i]; 715 } 716 else { 717 break; 718 } 719 } 720 721 return strMatched; 722 } 723 724 // -------------------------------------------------------------------- 725 // history 726 // -------------------------------------------------------------------- 727 ////////////////////////////////////////////// 728 void G4UItcsh::StoreHistory(G4String aCommand) 729 ////////////////////////////////////////////// 730 { 731 G4int i = currentHistoryNo % maxHistory; 732 if (i == 0) i = maxHistory; 733 734 commandHistory[i - 1] = aCommand; // 0-offset 735 currentHistoryNo++; 736 } 737 738 /////////////////////////////////////////////// 739 G4String G4UItcsh::RestoreHistory(G4int histNo) 740 /////////////////////////////////////////////// 741 { 742 if (histNo >= currentHistoryNo) return ""; 743 744 G4int index = histNo % maxHistory; 745 if (index == 0) index = maxHistory; 746 747 return commandHistory[index - 1]; // 0-offset 748 } 749 750 // -------------------------------------------------------------------- 751 // terminal mode 752 // -------------------------------------------------------------------- 753 /////////////////////////////////// 754 void G4UItcsh::SetTermToInputMode() 755 /////////////////////////////////// 756 { 757 termios tiosbuf = tios; 758 759 tiosbuf.c_iflag &= ~(BRKINT | ISTRIP); 760 tiosbuf.c_iflag |= (IGNBRK | IGNPAR); 761 tiosbuf.c_lflag &= ~(ICANON | IEXTEN | ECHO); 762 tiosbuf.c_cc[VMIN] = 1; 763 tiosbuf.c_cc[VTIME] = 0; 764 765 tcsetattr(0, TCSAFLUSH, &tiosbuf); 766 } 767 768 //////////////////////////// 769 void G4UItcsh::RestoreTerm() 770 //////////////////////////// 771 { 772 tcsetattr(0, TCSAFLUSH, &tios); 773 } 774 775 #endif 776