SST  15.1.0
StructuralSimulationToolkit
cmdLineEditor.h
1 // Copyright 2009-2025 NTESS. Under the terms
2 // of Contract DE-NA0003525 with NTESS, the U.S.
3 // Government retains certain rights in this software.
4 //
5 // Copyright (c) 2009-2025, NTESS
6 // All rights reserved.
7 //
8 // This file is part of the SST software package. For license
9 // information, see the LICENSE file in the top level directory of the
10 // distribution.
11 
12 #ifndef SST_CORE_IMPL_INTERACTIVE_CMDLINEEDITOR_H
13 #define SST_CORE_IMPL_INTERACTIVE_CMDLINEEDITOR_H
14 
15 #include <cctype>
16 #include <fstream>
17 #include <functional>
18 #include <list>
19 #include <map>
20 #include <string>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <vector>
24 
25 /**
26  * Common write to console
27  */
28 inline void
29 writeStr(const std::string& msg)
30 {
31  (void)!write(STDOUT_FILENO, msg.data(), msg.size());
32 }
33 
34 /**
35  * The command line editor uses termios to detect key presses
36  * and perform auto-completions. Upon entering the editor,
37  * the current terminal settings are saved and we enter a
38  * "raw" terminal mode. While in raw terminal mode it is
39  * critical to ensure that exclusivel use read and write commands
40  * for std:out access. Mixing iostream access can corrupt the
41  * buffers.
42  */
44 {
45 public:
46  static constexpr char esc_char = '\x1B';
47  static constexpr char tab_char = '\x9';
48  static constexpr char lf_char = '\xa';
49  static constexpr char bs_char = '\x7f';
50  static constexpr char ctrl_a = '\x1';
51  static constexpr char ctrl_b = '\x2';
52  static constexpr char ctrl_d = '\x4';
53  static constexpr char ctrl_e = '\x5';
54  static constexpr char ctrl_f = '\x6';
55  static constexpr char ctrl_k = '\xb';
56 
57  const std::string arrow_up = "[A";
58  const std::string arrow_dn = "[B";
59  const std::string arrow_rt = "[C";
60  const std::string arrow_lf = "[D";
61  const std::map<std::string, std::string> arrowKeyMap = {
62  { arrow_up, "Up" },
63  { arrow_dn, "Down" },
64  { arrow_rt, "Right" },
65  { arrow_lf, "Left" },
66  };
67 
68  const std::string clear_line_ctl = "\x1B[2K";
69  const std::string move_left_ctl = "\x1B[1D";
70  const std::string move_right_ctl = "\x1B[1C";
71  const std::string esc_ctl = "\x1B["; //"\x1b[5G" moves to column 5
72  const std::string move_up_ctl = "\x1B[1F";
73 
74  const std::string prompt = "> ";
75  const std::string prompt_clear = "\x1B[2K\r> ";
76 
77  static constexpr int max_line_size = 2048;
78 
79  CmdLineEditor();
80  virtual ~CmdLineEditor() = default;
81  void redraw_line(const std::string& s);
82  void getline(const std::vector<std::string>& cmdHistory, std::string& newcmd);
83 
84  // Auto-completion support
85  void set_cmd_strings(const std::list<std::string>& sortedStrings);
86  void set_listing_callback(std::function<void(std::list<std::string>&)> callback) { listing_callback_ = callback; }
87 
88  // debug helper
89  std::ofstream dbgFile;
90 
91 private:
92  termios originalTerm;
93  std::list<std::string> cmdStrings = {};
94  std::function<void(std::list<std::string>& callback)> listing_callback_ = nullptr;
95 
96  int curpos = -1;
97  int checktty(int rc);
98  int setRawMode();
99  int restoreTermMode();
100  bool read2chars(char (&seq)[3]);
101  void move_cursor_left();
102  void move_cursor_right(int slen);
103  void auto_complete(std::string& cmd);
104  bool selectMatches(const std::list<std::string>& list, const std::string& searchfor,
105  std::vector<std::string>& matches, std::string& newcmd);
106 
107 private:
108  // yet more string helpers
109  static bool compareCharCaseInsensitive(char c1, char c2)
110  {
111  return std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2));
112  };
113 
114  // exact, but case insenstive, match
115  // static bool compareStringCaseInsensitive(const std::string& s1, const std::string& s2) {
116  // if (s1.length() != s2.length())
117  // return false;
118  // return std::equal(s1.begin(), s1.end(), s2.begin(), compareCharCaseInsensitive);
119  // };
120 
121  // match if begining of second string matches the first
122  static bool matchBeginStringCaseInsensitive(const std::string& searchfor, const std::string& longstr)
123  {
124  if ( longstr.length() < searchfor.length() ) return false;
125  std::string matchstr = longstr.substr(0, searchfor.length());
126  return std::equal(searchfor.begin(), searchfor.end(), matchstr.begin(), compareCharCaseInsensitive);
127  }
128 
129  // Bug: Why is this escape code injected at the 7th char after verbose printing of 0x...?
130  void flush_bad_escape();
131 };
132 
133 #endif // SST_CORE_IMPL_INTERACTIVE_CMDLINEEDITOR_H
The command line editor uses termios to detect key presses and perform auto-completions.
Definition: cmdLineEditor.h:43