Coverage Report

Created: 2025-07-18 06:08

/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
}