OpenMPTL - Helper Library
C++ Microprocessor Template Library
terminal.hpp
Go to the documentation of this file.
1 /*
2  * OpenMPTL - C++ Microprocessor Template Library
3  *
4  * Copyright (C) 2012-2017 Axel Burri <axel@tty0.ch>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #ifndef TERMINAL_HPP_INCLUDED
22 #define TERMINAL_HPP_INCLUDED
23 
24 #include <fifo_stream.hpp>
25 #include <cstring> // strcmp
26 #include <type_traits>
27 
28 namespace mptl {
29 
30 namespace i18n { namespace terminal {
31  static constexpr const char * cmd_notfound = ": command not found (type \"help\" for a list of available commands)";
32  static constexpr const char * cmd_list = "List of commands:";
33 } } // namespace i18n::terminal
34 
35 
36 // ----------------------------------------------------------------------------
37 // terminal
38 //
39 
40 /**
41  * Simple vt100-like terminal.
42  *
43  * Parses and executes commands from mptl::terminal_hook_list<...>,
44  * and accepts simple single-word commands (see process_input()).
45  *
46  * Operates on mptl::fifo_stream<> for input/output, and provides a
47  * very basic stream interface from poorman::ostream<>:
48  *
49  * mptl::terminal<...> term;
50  * term.tx_stream << "hello, var=" << myvar << poorman::endl;
51  *
52  * Template arguments:
53  *
54  * - Tp: stream_device_type, must provide:
55  * - fifo_type (of type: fifo<>)
56  * - static void flush()
57  * - static bool crlf
58  */
59 template<typename Tp, bool _terminal_echo = true>
60 class terminal
61 {
62 public:
63 
64  using stream_device_type = Tp;
65  using char_type = typename stream_device_type::fifo_type::char_type;
66 
68 
70 
71  static constexpr bool terminal_echo = _terminal_echo;
72  static constexpr const char * newline = "\r\n";
73  static constexpr const char * prompt = "# ";
74  static constexpr std::size_t cmd_buf_size = 80; // TODO: use cmd_hooks::cmd_buf_size, which is the maximum of all command sizes (can be computed at compile-time!)
75 
76 private:
77 
78  char_type cmd_buf[cmd_buf_size];
79  unsigned cmd_index = 0;
80 
81 public:
82 
83  using resources = typename stream_device_type::resources;
84 
85  terminal() : tx_stream(stream_device_type::tx_fifo) { }
86 
87  void open() const {
88  stream_device_type::open();
89  }
90 
91  void close() {
92  stream_device_type::close();
93  cmd_index = 0;
94  }
95 
96  template<typename cmd_hooks>
97  void process_input(void)
98  {
99  bool flush_tx = false;
100  char c;
101  while(stream_device_type::rx_fifo.pop(c)) {
102  flush_tx = true;
103  if(c == 13) // CR
104  {
105  if(terminal_echo)
106  tx_stream << newline;
107 
108  cmd_buf[cmd_index] = 0;
109 
110  if(cmd_index) {
111  cmd_hooks::template execute<cmd_hooks>(cmd_buf, tx_stream);
112  }
113  tx_stream << prompt;
114  cmd_index = 0;
115  }
116  else if((c >= 32) && (c <= 126) && (cmd_index < cmd_buf_size))
117  {
118  if(terminal_echo)
119  tx_stream.put(c);
120 
121  cmd_buf[cmd_index++] = c;
122  }
123  }
124  if(flush_tx)
125  tx_stream.flush();
126  }
127 };
128 
129 
130 // ----------------------------------------------------------------------------
131 // terminal_hook
132 //
133 
135 };
136 
137 
138 // ----------------------------------------------------------------------------
139 // terminal_hook_list
140 //
141 
142 namespace mpl
143 {
144  template<std::size_t... Args>
145  struct max {
146  static constexpr std::size_t value = 0;
147  };
148 
149  template<std::size_t T, std::size_t... Args>
150  struct max<T, Args...> {
151  static constexpr std::size_t value = T > max<Args...>::value ? T : max<Args...>::value;
152  };
153 }
154 
155 
156 template<typename... Args>
158 
159 template<>
161 {
162  template<typename HL>
163  static void execute(const char * cmd_buf, poorman::ostream<char> & cout) {
164  if(strcmp("help", cmd_buf) == 0) {
166  HL::template list<HL>(cout);
167  }
168  else {
169  cout << cmd_buf << i18n::terminal::cmd_notfound << poorman::endl;
170  }
171  }
172 
173  template<typename HL>
174  static void list(poorman::ostream<char> &) { }
175 };
176 
177 template<typename T, typename... Args>
178 struct terminal_hook_list<T, Args...> {
179 
180  template<typename HL>
181  static void execute(const char * cmd_buf, poorman::ostream<char> & cout) {
182  if(strcmp(T::cmd, cmd_buf) == 0) {
183  T().run(cout);
184  }
185  else {
186  terminal_hook_list<Args...>::template execute<HL>(cmd_buf, cout);
187  }
188  }
189 
190 #ifdef CONFIG_CLANG
191  // for some reason, clang-3.3 does not treat strlen() as constexpr
192  static constexpr unsigned cmd_maxlen = 8;
193 #else
194  /** maximum length of all commands (minimum 8, for formatting help text) */
195  static constexpr unsigned cmd_maxlen = mpl::max<8, strlen(T::cmd), strlen(Args::cmd)...>::value;
196 #endif
197 
198  template<typename HL>
199  static void list(poorman::ostream<char> & cout) {
200  cout << " " << T::cmd;
201  for(int n = HL::cmd_maxlen - strlen(T::cmd) + 3 ; n > 0; n--)
202  cout.put(' ');
203  cout << T::desc << poorman::endl;
204 
205  terminal_hook_list<Args...>::template list<HL>(cout);
206  }
207 };
208 
209 } // namespace mptl
210 
211 #endif // TERMINAL_HPP_INCLUDED
Definition: terminal.hpp:157
void open() const
Definition: terminal.hpp:87
ostream< Tp > & endl(ostream< Tp > &st)
manipulator, outputs newline and flushes the output stream
Definition: poorman_ostream.hpp:75
Definition: poorman_ostream.hpp:29
typename stream_device_type::fifo_type::char_type char_type
Definition: terminal.hpp:65
typename stream_device_type::resources resources
Definition: terminal.hpp:83
static void execute(const char *cmd_buf, poorman::ostream< char > &cout)
Definition: terminal.hpp:181
virtual ostream & put(char_type c)=0
static void execute(const char *cmd_buf, poorman::ostream< char > &cout)
Definition: terminal.hpp:163
Simple vt100-like terminal.
Definition: terminal.hpp:60
poorman::ostream< char_type > & flush()
Definition: fifo_stream.hpp:55
static constexpr const char * cmd_list
Definition: terminal.hpp:32
tx_stream_type tx_stream
Definition: terminal.hpp:69
terminal()
Definition: terminal.hpp:85
Tp stream_device_type
Definition: terminal.hpp:64
poorman::ostream< char_type > & put(char_type c)
Definition: fifo_stream.hpp:40
static void list(poorman::ostream< char > &cout)
Definition: terminal.hpp:199
void process_input(void)
Definition: terminal.hpp:97
static void list(poorman::ostream< char > &)
Definition: terminal.hpp:174
static constexpr const char * cmd_notfound
Definition: terminal.hpp:31
Definition: terminal.hpp:134
void close()
Definition: terminal.hpp:91