SST 16.0.0
Structural Simulation Toolkit
cmdLineEditor.h
1// Copyright 2009-2026 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-2026, 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 */
28inline void
29writeStr(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 */
43class CmdLineEditor
44{
45public:
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 // tab behaviors
92 enum TAB_STATE { FILL_COMMON, LIST_COMMON };
93
94private:
95 termios originalTerm;
96 std::list<std::string> cmdStrings = {};
97 std::function<void(std::list<std::string>& callback)> listing_callback_ = nullptr;
98
99 int curpos = -1;
100 TAB_STATE tab_state = TAB_STATE::FILL_COMMON;
101 int checktty(int rc);
102 int setRawMode();
103 int restoreTermMode();
104 bool read2chars(char (&seq)[3]);
105 void move_cursor_left();
106 void move_cursor_right(int slen);
107 void auto_complete(std::string& cmd);
108 bool selectMatches(const std::list<std::string>& list, const std::string& searchfor,
109 std::vector<std::string>& matches, std::string& newcmd);
110 std::string findLongestCommonPrefix(const std::vector<std::string>& strings);
111
112private:
113 // yet more string helpers
114 static bool compareCharCaseInsensitive(char c1, char c2)
115 {
116 return std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2));
117 };
118
119 // exact, but case insenstive, match
120 // static bool compareStringCaseInsensitive(const std::string& s1, const std::string& s2) {
121 // if (s1.length() != s2.length())
122 // return false;
123 // return std::equal(s1.begin(), s1.end(), s2.begin(), compareCharCaseInsensitive);
124 // };
125
126 // match if begining of second string matches the first
127 static bool matchBeginStringCaseInsensitive(const std::string& searchfor, const std::string& longstr)
128 {
129 if ( longstr.length() < searchfor.length() ) return false;
130 std::string matchstr = longstr.substr(0, searchfor.length());
131 return std::equal(searchfor.begin(), searchfor.end(), matchstr.begin(), compareCharCaseInsensitive);
132 }
133
134 // Bug: Why is this escape code injected at the 7th char after verbose printing of 0x...?
135 void flush_bad_escape();
136};
137
138#endif // SST_CORE_IMPL_INTERACTIVE_CMDLINEEDITOR_H