/src/mosh/src/terminal/terminaldispatcher.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Mosh: the mobile shell |
3 | | Copyright 2012 Keith Winstein |
4 | | |
5 | | This program is free software: you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation, either version 3 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | In addition, as a special exception, the copyright holders give |
19 | | permission to link the code of portions of this program with the |
20 | | OpenSSL library under certain conditions as described in each |
21 | | individual source file, and distribute linked combinations including |
22 | | the two. |
23 | | |
24 | | You must obey the GNU General Public License in all respects for all |
25 | | of the code used other than OpenSSL. If you modify file(s) with this |
26 | | exception, you may extend this exception to your version of the |
27 | | file(s), but you are not obligated to do so. If you do not wish to do |
28 | | so, delete this exception statement from your version. If you delete |
29 | | this exception statement from all source files in the program, then |
30 | | also delete it here. |
31 | | */ |
32 | | |
33 | | #include <cassert> |
34 | | #include <cerrno> |
35 | | #include <cstdio> |
36 | | #include <cstdlib> |
37 | | #include <cstring> |
38 | | |
39 | | #include "src/terminal/parseraction.h" |
40 | | #include "src/terminal/terminalframebuffer.h" |
41 | | #include "terminaldispatcher.h" |
42 | | |
43 | | using namespace Terminal; |
44 | | |
45 | | static const size_t MAXIMUM_CLIPBOARD_SIZE = 16 * 1024; |
46 | | |
47 | | Dispatcher::Dispatcher() |
48 | 117 | : params(), parsed_params(), parsed( false ), dispatch_chars(), OSC_string(), terminal_to_host() |
49 | 117 | {} |
50 | | |
51 | | void Dispatcher::newparamchar( const Parser::Param* act ) |
52 | 0 | { |
53 | 0 | assert( act->char_present ); |
54 | 0 | assert( ( act->ch == ';' ) || ( ( act->ch >= '0' ) && ( act->ch <= '9' ) ) ); |
55 | 0 | if ( params.length() < 100 ) { |
56 | | /* enough for 16 five-char params plus 15 semicolons */ |
57 | 0 | params.push_back( act->ch ); |
58 | 0 | } |
59 | 0 | parsed = false; |
60 | 0 | } |
61 | | |
62 | | void Dispatcher::collect( const Parser::Collect* act ) |
63 | 0 | { |
64 | 0 | assert( act->char_present ); |
65 | 0 | if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */ |
66 | 0 | && ( act->ch <= 255 ) ) { /* ignore non-8-bit */ |
67 | 0 | dispatch_chars.push_back( act->ch ); |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | | void Dispatcher::clear( const Parser::Clear* act __attribute( ( unused ) ) ) |
72 | 0 | { |
73 | 0 | params.clear(); |
74 | 0 | dispatch_chars.clear(); |
75 | 0 | parsed = false; |
76 | 0 | } |
77 | | |
78 | | void Dispatcher::parse_params( void ) |
79 | 0 | { |
80 | 0 | if ( parsed ) { |
81 | 0 | return; |
82 | 0 | } |
83 | | |
84 | 0 | parsed_params.clear(); |
85 | 0 | const char* str = params.c_str(); |
86 | 0 | const char* segment_begin = str; |
87 | |
|
88 | 0 | while ( 1 ) { |
89 | 0 | const char* segment_end = strchr( segment_begin, ';' ); |
90 | 0 | if ( segment_end == NULL ) { |
91 | 0 | break; |
92 | 0 | } |
93 | | |
94 | 0 | errno = 0; |
95 | 0 | char* endptr; |
96 | 0 | long val = strtol( segment_begin, &endptr, 10 ); |
97 | 0 | if ( endptr == segment_begin ) { |
98 | 0 | val = -1; |
99 | 0 | } |
100 | |
|
101 | 0 | if ( val > PARAM_MAX || errno == ERANGE ) { |
102 | 0 | val = -1; |
103 | 0 | errno = 0; |
104 | 0 | } |
105 | |
|
106 | 0 | if ( errno == 0 || segment_begin == endptr ) { |
107 | 0 | parsed_params.push_back( val ); |
108 | 0 | } |
109 | |
|
110 | 0 | segment_begin = segment_end + 1; |
111 | 0 | } |
112 | | |
113 | | /* get last param */ |
114 | 0 | errno = 0; |
115 | 0 | char* endptr; |
116 | 0 | long val = strtol( segment_begin, &endptr, 10 ); |
117 | 0 | if ( endptr == segment_begin ) { |
118 | 0 | val = -1; |
119 | 0 | } |
120 | |
|
121 | 0 | if ( val > PARAM_MAX || errno == ERANGE ) { |
122 | 0 | val = -1; |
123 | 0 | errno = 0; |
124 | 0 | } |
125 | |
|
126 | 0 | if ( errno == 0 || segment_begin == endptr ) { |
127 | 0 | parsed_params.push_back( val ); |
128 | 0 | } |
129 | |
|
130 | 0 | parsed = true; |
131 | 0 | } |
132 | | |
133 | | int Dispatcher::getparam( size_t N, int defaultval ) |
134 | 0 | { |
135 | 0 | int ret = defaultval; |
136 | 0 | if ( !parsed ) { |
137 | 0 | parse_params(); |
138 | 0 | } |
139 | |
|
140 | 0 | if ( parsed_params.size() > N ) { |
141 | 0 | ret = parsed_params[N]; |
142 | 0 | } |
143 | |
|
144 | 0 | if ( ret < 1 ) |
145 | 0 | ret = defaultval; |
146 | |
|
147 | 0 | return ret; |
148 | 0 | } |
149 | | |
150 | | int Dispatcher::param_count( void ) |
151 | 0 | { |
152 | 0 | if ( !parsed ) { |
153 | 0 | parse_params(); |
154 | 0 | } |
155 | |
|
156 | 0 | return parsed_params.size(); |
157 | 0 | } |
158 | | |
159 | | std::string Dispatcher::str( void ) |
160 | 0 | { |
161 | 0 | char assum[64]; |
162 | 0 | snprintf( assum, 64, "[dispatch=\"%s\" params=\"%s\"]", dispatch_chars.c_str(), params.c_str() ); |
163 | 0 | return std::string( assum ); |
164 | 0 | } |
165 | | |
166 | | /* construct on first use to avoid static initialization order crash */ |
167 | | DispatchRegistry& Terminal::get_global_dispatch_registry( void ) |
168 | 184 | { |
169 | 184 | static DispatchRegistry global_dispatch_registry; |
170 | 184 | return global_dispatch_registry; |
171 | 184 | } |
172 | | |
173 | | static void register_function( Function_Type type, const std::string& dispatch_chars, Function f ) |
174 | 184 | { |
175 | 184 | switch ( type ) { |
176 | 16 | case ESCAPE: |
177 | 16 | get_global_dispatch_registry().escape.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); |
178 | 16 | break; |
179 | 124 | case CSI: |
180 | 124 | get_global_dispatch_registry().CSI.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); |
181 | 124 | break; |
182 | 44 | case CONTROL: |
183 | 44 | get_global_dispatch_registry().control.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); |
184 | 44 | break; |
185 | 184 | } |
186 | 184 | } |
187 | | |
188 | | Function::Function( Function_Type type, |
189 | | const std::string& dispatch_chars, |
190 | | void ( *s_function )( Framebuffer*, Dispatcher* ), |
191 | | bool s_clears_wrap_state ) |
192 | 184 | : function( s_function ), clears_wrap_state( s_clears_wrap_state ) |
193 | 184 | { |
194 | 184 | register_function( type, dispatch_chars, *this ); |
195 | 184 | } |
196 | | |
197 | | void Dispatcher::dispatch( Function_Type type, const Parser::Action* act, Framebuffer* fb ) |
198 | 0 | { |
199 | | /* add final char to dispatch key */ |
200 | 0 | if ( ( type == ESCAPE ) || ( type == CSI ) ) { |
201 | 0 | assert( act->char_present ); |
202 | 0 | Parser::Collect act2; |
203 | 0 | act2.char_present = true; |
204 | 0 | act2.ch = act->ch; |
205 | 0 | collect( &act2 ); |
206 | 0 | } |
207 | | |
208 | 0 | dispatch_map_t* map = NULL; |
209 | 0 | switch ( type ) { |
210 | 0 | case ESCAPE: |
211 | 0 | map = &get_global_dispatch_registry().escape; |
212 | 0 | break; |
213 | 0 | case CSI: |
214 | 0 | map = &get_global_dispatch_registry().CSI; |
215 | 0 | break; |
216 | 0 | case CONTROL: |
217 | 0 | map = &get_global_dispatch_registry().control; |
218 | 0 | break; |
219 | 0 | } |
220 | | |
221 | 0 | std::string key = dispatch_chars; |
222 | 0 | if ( type == CONTROL ) { |
223 | 0 | assert( act->ch <= 255 ); |
224 | 0 | char ctrlstr[2] = { (char)act->ch, 0 }; |
225 | 0 | key = std::string( ctrlstr, 1 ); |
226 | 0 | } |
227 | | |
228 | 0 | dispatch_map_t::const_iterator i = map->find( key ); |
229 | 0 | if ( i == map->end() ) { |
230 | | /* unknown function */ |
231 | 0 | fb->ds.next_print_will_wrap = false; |
232 | 0 | return; |
233 | 0 | } |
234 | 0 | if ( i->second.clears_wrap_state ) { |
235 | 0 | fb->ds.next_print_will_wrap = false; |
236 | 0 | } |
237 | 0 | i->second.function( fb, this ); |
238 | 0 | } |
239 | | |
240 | | void Dispatcher::OSC_put( const Parser::OSC_Put* act ) |
241 | 0 | { |
242 | 0 | assert( act->char_present ); |
243 | 0 | if ( OSC_string.size() < MAXIMUM_CLIPBOARD_SIZE ) { |
244 | 0 | OSC_string.push_back( act->ch ); |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | void Dispatcher::OSC_start( const Parser::OSC_Start* act __attribute( ( unused ) ) ) |
249 | 0 | { |
250 | 0 | OSC_string.clear(); |
251 | 0 | } |
252 | | |
253 | | bool Dispatcher::operator==( const Dispatcher& x ) const |
254 | 0 | { |
255 | 0 | return ( params == x.params ) && ( parsed_params == x.parsed_params ) && ( parsed == x.parsed ) |
256 | 0 | && ( dispatch_chars == x.dispatch_chars ) && ( OSC_string == x.OSC_string ) |
257 | 0 | && ( terminal_to_host == x.terminal_to_host ); |
258 | 0 | } |