1 /*
2  * Copyright (c) 2017-2018, Marcin Konarski (amok at codestation.org)
3  * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
4  * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *	 * Redistributions of source code must retain the above copyright notice,
12  *		 this list of conditions and the following disclaimer.
13  *	 * Redistributions in binary form must reproduce the above copyright
14  *		 notice, this list of conditions and the following disclaimer in the
15  *		 documentation and/or other materials provided with the distribution.
16  *	 * Neither the name of Redis nor the names of its contributors may be used
17  *		 to endorse or promote products derived from this software without
18  *		 specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  * line editing lib needs to be 20,000 lines of C code.
33  *
34  * You can find the latest source code at:
35  *
36  *	 http://github.com/antirez/linenoise
37  *
38  * Does a number of crazy assumptions that happen to be true in 99.9999% of
39  * the 2010 UNIX computers around.
40  *
41  * References:
42  * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
43  * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
44  *
45  * Todo list:
46  * - Switch to gets() if $TERM is something we can't support.
47  * - Filter bogus Ctrl+<char> combinations.
48  * - Win32 support
49  *
50  * Bloat:
51  * - Completion?
52  * - History search like Ctrl+r in readline?
53  *
54  * List of escape sequences used by this program, we do everything just
55  * with three sequences. In order to be so cheap we may have some
56  * flickering effect with some slow terminal, but the lesser sequences
57  * the more compatible.
58  *
59  * CHA (Cursor Horizontal Absolute)
60  *		Sequence: ESC [ n G
61  *		Effect: moves cursor to column n (1 based)
62  *
63  * EL (Erase Line)
64  *		Sequence: ESC [ n K
65  *		Effect: if n is 0 or missing, clear from cursor to end of line
66  *		Effect: if n is 1, clear from beginning of line to cursor
67  *		Effect: if n is 2, clear entire line
68  *
69  * CUF (Cursor Forward)
70  *		Sequence: ESC [ n C
71  *		Effect: moves cursor forward of n chars
72  *
73  * The following are used to clear the screen: ESC [ H ESC [ 2 J
74  * This is actually composed of two sequences:
75  *
76  * cursorhome
77  *		Sequence: ESC [ H
78  *		Effect: moves the cursor to upper left corner
79  *
80  * ED2 (Clear entire screen)
81  *		Sequence: ESC [ 2 J
82  *		Effect: clear the whole screen
83  *
84  */
85 
86 #include <algorithm>
87 #include <cstdarg>
88 
89 #ifdef _WIN32
90 
91 #include <io.h>
92 #define STDIN_FILENO 0
93 
94 #else /* _WIN32 */
95 
96 #include <signal.h>
97 #include <unistd.h>
98 #include <sys/stat.h>
99 
100 #endif /* _WIN32 */
101 
102 #include "replxx.h"
103 #include "replxx.hxx"
104 #include "replxx_impl.hxx"
105 #include "history.hxx"
106 
107 static_assert(
108 	static_cast<int>( replxx::Replxx::ACTION::SEND_EOF ) == static_cast<int>( REPLXX_ACTION_SEND_EOF ),
109 	"C and C++ `ACTION` APIs are missaligned!"
110 );
111 
112 static_assert(
113 	static_cast<int>( replxx::Replxx::KEY::PASTE_FINISH ) == static_cast<int>( REPLXX_KEY_PASTE_FINISH ),
114 	"C and C++ `KEY` APIs are missaligned!"
115 );
116 
117 using namespace std;
118 using namespace std::placeholders;
119 using namespace replxx;
120 
121 namespace replxx {
122 
123 namespace {
delete_ReplxxImpl(Replxx::ReplxxImpl * impl_)124 void delete_ReplxxImpl( Replxx::ReplxxImpl* impl_ ) {
125 	delete impl_;
126 }
127 }
128 
Replxx(void)129 Replxx::Replxx( void )
130 	: _impl( new Replxx::ReplxxImpl( nullptr, nullptr, nullptr ), delete_ReplxxImpl ) {
131 }
132 
set_completion_callback(completion_callback_t const & fn)133 void Replxx::set_completion_callback( completion_callback_t const& fn ) {
134 	_impl->set_completion_callback( fn );
135 }
136 
set_modify_callback(modify_callback_t const & fn)137 void Replxx::set_modify_callback( modify_callback_t const& fn ) {
138 	_impl->set_modify_callback( fn );
139 }
140 
set_highlighter_callback(highlighter_callback_t const & fn)141 void Replxx::set_highlighter_callback( highlighter_callback_t const& fn ) {
142 	_impl->set_highlighter_callback( fn );
143 }
144 
set_hint_callback(hint_callback_t const & fn)145 void Replxx::set_hint_callback( hint_callback_t const& fn ) {
146 	_impl->set_hint_callback( fn );
147 }
148 
input(std::string const & prompt)149 char const* Replxx::input( std::string const& prompt ) {
150 	return ( _impl->input( prompt ) );
151 }
152 
history_add(std::string const & line)153 void Replxx::history_add( std::string const& line ) {
154 	_impl->history_add( line );
155 }
156 
history_sync(std::string const & filename)157 bool Replxx::history_sync( std::string const& filename ) {
158 	return ( _impl->history_sync( filename ) );
159 }
160 
history_save(std::string const & filename)161 bool Replxx::history_save( std::string const& filename ) {
162 	return ( _impl->history_save( filename ) );
163 }
164 
history_load(std::string const & filename)165 bool Replxx::history_load( std::string const& filename ) {
166 	return ( _impl->history_load( filename ) );
167 }
168 
history_clear(void)169 void Replxx::history_clear( void ) {
170 	_impl->history_clear();
171 }
172 
history_size(void) const173 int Replxx::history_size( void ) const {
174 	return ( _impl->history_size() );
175 }
176 
history_scan(void) const177 Replxx::HistoryScan Replxx::history_scan( void ) const {
178 	return ( _impl->history_scan() );
179 }
180 
set_preload_buffer(std::string const & preloadText)181 void Replxx::set_preload_buffer( std::string const& preloadText ) {
182 	_impl->set_preload_buffer( preloadText );
183 }
184 
set_word_break_characters(char const * wordBreakers)185 void Replxx::set_word_break_characters( char const* wordBreakers ) {
186 	_impl->set_word_break_characters( wordBreakers );
187 }
188 
set_max_hint_rows(int count)189 void Replxx::set_max_hint_rows( int count ) {
190 	_impl->set_max_hint_rows( count );
191 }
192 
set_hint_delay(int milliseconds)193 void Replxx::set_hint_delay( int milliseconds ) {
194 	_impl->set_hint_delay( milliseconds );
195 }
196 
set_completion_count_cutoff(int count)197 void Replxx::set_completion_count_cutoff( int count ) {
198 	_impl->set_completion_count_cutoff( count );
199 }
200 
set_double_tab_completion(bool val)201 void Replxx::set_double_tab_completion( bool val ) {
202 	_impl->set_double_tab_completion( val );
203 }
204 
set_complete_on_empty(bool val)205 void Replxx::set_complete_on_empty( bool val ) {
206 	_impl->set_complete_on_empty( val );
207 }
208 
set_beep_on_ambiguous_completion(bool val)209 void Replxx::set_beep_on_ambiguous_completion( bool val ) {
210 	_impl->set_beep_on_ambiguous_completion( val );
211 }
212 
set_immediate_completion(bool val)213 void Replxx::set_immediate_completion( bool val ) {
214 	_impl->set_immediate_completion( val );
215 }
216 
set_unique_history(bool val)217 void Replxx::set_unique_history( bool val ) {
218 	_impl->set_unique_history( val );
219 }
220 
set_no_color(bool val)221 void Replxx::set_no_color( bool val ) {
222 	_impl->set_no_color( val );
223 }
224 
set_max_history_size(int len)225 void Replxx::set_max_history_size( int len ) {
226 	_impl->set_max_history_size( len );
227 }
228 
clear_screen(void)229 void Replxx::clear_screen( void ) {
230 	_impl->clear_screen( 0 );
231 }
232 
emulate_key_press(char32_t keyPress_)233 void Replxx::emulate_key_press( char32_t keyPress_ ) {
234 	_impl->emulate_key_press( keyPress_ );
235 }
236 
invoke(ACTION action_,char32_t keyPress_)237 Replxx::ACTION_RESULT Replxx::invoke( ACTION action_, char32_t keyPress_ ) {
238 	return ( _impl->invoke( action_, keyPress_ ) );
239 }
240 
bind_key(char32_t keyPress_,key_press_handler_t handler_)241 void Replxx::bind_key( char32_t keyPress_, key_press_handler_t handler_ ) {
242 	_impl->bind_key( keyPress_, handler_ );
243 }
244 
bind_key_internal(char32_t keyPress_,char const * actionName_)245 void Replxx::bind_key_internal( char32_t keyPress_, char const* actionName_ ) {
246 	_impl->bind_key_internal( keyPress_, actionName_ );
247 }
248 
get_state(void) const249 Replxx::State Replxx::get_state( void ) const {
250 	return ( _impl->get_state() );
251 }
252 
set_state(Replxx::State const & state_)253 void Replxx::set_state( Replxx::State const& state_ ) {
254 	_impl->set_state( state_ );
255 }
256 
install_window_change_handler(void)257 int Replxx::install_window_change_handler( void ) {
258 	return ( _impl->install_window_change_handler() );
259 }
260 
enable_bracketed_paste(void)261 void Replxx::enable_bracketed_paste( void ) {
262 	_impl->enable_bracketed_paste();
263 }
264 
disable_bracketed_paste(void)265 void Replxx::disable_bracketed_paste( void ) {
266 	_impl->disable_bracketed_paste();
267 }
268 
print(char const * format_,...)269 void Replxx::print( char const* format_, ... ) {
270 	::std::va_list ap;
271 	va_start( ap, format_ );
272 	int size = static_cast<int>( vsnprintf( nullptr, 0, format_, ap ) );
273 	va_end( ap );
274 	va_start( ap, format_ );
275 	unique_ptr<char[]> buf( new char[size + 1] );
276 	vsnprintf( buf.get(), static_cast<size_t>( size + 1 ), format_, ap );
277 	va_end( ap );
278 	return ( _impl->print( buf.get(), size ) );
279 }
280 
write(char const * str,int length)281 void Replxx::write( char const* str, int length ) {
282 	return ( _impl->print( str, length ) );
283 }
284 
285 }
286 
replxx_init()287 ::Replxx* replxx_init() {
288 	typedef ::Replxx* replxx_data_t;
289 	return ( reinterpret_cast<replxx_data_t>( new replxx::Replxx::ReplxxImpl( nullptr, nullptr, nullptr ) ) );
290 }
291 
replxx_end(::Replxx * replxx_)292 void replxx_end( ::Replxx* replxx_ ) {
293 	delete reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ );
294 }
295 
replxx_clear_screen(::Replxx * replxx_)296 void replxx_clear_screen( ::Replxx* replxx_ ) {
297 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
298 	replxx->clear_screen( 0 );
299 }
300 
replxx_emulate_key_press(::Replxx * replxx_,int unsigned keyPress_)301 void replxx_emulate_key_press( ::Replxx* replxx_, int unsigned keyPress_ ) {
302 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
303 	replxx->emulate_key_press( keyPress_ );
304 }
305 
replxx_invoke(::Replxx * replxx_,ReplxxAction action_,int unsigned keyPress_)306 ReplxxActionResult replxx_invoke( ::Replxx* replxx_, ReplxxAction action_, int unsigned keyPress_ ) {
307 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
308 	return ( static_cast<ReplxxActionResult>( replxx->invoke( static_cast<replxx::Replxx::ACTION>( action_ ), keyPress_ ) ) );
309 }
310 
key_press_handler_forwarder(key_press_handler_t handler_,char32_t code_,void * userData_)311 replxx::Replxx::ACTION_RESULT key_press_handler_forwarder( key_press_handler_t handler_, char32_t code_, void* userData_ ) {
312 	return ( static_cast<replxx::Replxx::ACTION_RESULT>( handler_( code_, userData_ ) ) );
313 }
314 
replxx_bind_key(::Replxx * replxx_,int code_,key_press_handler_t handler_,void * userData_)315 void replxx_bind_key( ::Replxx* replxx_, int code_, key_press_handler_t handler_, void* userData_ ) {
316 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
317 	replxx->bind_key( code_, std::bind( key_press_handler_forwarder, handler_, _1, userData_ ) );
318 }
319 
replxx_bind_key_internal(::Replxx * replxx_,int code_,char const * actionName_)320 int replxx_bind_key_internal( ::Replxx* replxx_, int code_, char const* actionName_ ) {
321 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
322 	try {
323 		replxx->bind_key_internal( code_, actionName_ );
324 	} catch ( ... ) {
325 		return ( -1 );
326 	}
327 	return ( 0 );
328 }
329 
replxx_get_state(::Replxx * replxx_,ReplxxState * state)330 void replxx_get_state( ::Replxx* replxx_, ReplxxState* state ) {
331 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
332 	replxx::Replxx::State s( replxx->get_state() );
333 	state->text = s.text();
334 	state->cursorPosition = s.cursor_position();
335 }
336 
replxx_set_state(::Replxx * replxx_,ReplxxState * state)337 void replxx_set_state( ::Replxx* replxx_, ReplxxState* state ) {
338 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
339 	replxx->set_state( replxx::Replxx::State( state->text, state->cursorPosition ) );
340 }
341 
342 /**
343  * replxx_set_preload_buffer provides text to be inserted into the command buffer
344  *
345  * the provided text will be processed to be usable and will be used to preload
346  * the input buffer on the next call to replxx_input()
347  *
348  * @param preloadText text to begin with on the next call to replxx_input()
349  */
replxx_set_preload_buffer(::Replxx * replxx_,const char * preloadText)350 void replxx_set_preload_buffer(::Replxx* replxx_, const char* preloadText) {
351 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
352 	replxx->set_preload_buffer( preloadText ? preloadText : "" );
353 }
354 
355 /**
356  * replxx_input is a readline replacement.
357  *
358  * call it with a prompt to display and it will return a line of input from the
359  * user
360  *
361  * @param prompt text of prompt to display to the user
362  * @return the returned string is managed by replxx library
363  * and it must NOT be freed in the client.
364  */
replxx_input(::Replxx * replxx_,const char * prompt)365 char const* replxx_input( ::Replxx* replxx_, const char* prompt ) {
366 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
367 	return ( replxx->input( prompt ) );
368 }
369 
replxx_print(::Replxx * replxx_,char const * format_,...)370 int replxx_print( ::Replxx* replxx_, char const* format_, ... ) {
371 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
372 	::std::va_list ap;
373 	va_start( ap, format_ );
374 	int size = static_cast<int>( vsnprintf( nullptr, 0, format_, ap ) );
375 	va_end( ap );
376 	va_start( ap, format_ );
377 	unique_ptr<char[]> buf( new char[size + 1] );
378 	vsnprintf( buf.get(), static_cast<size_t>( size + 1 ), format_, ap );
379 	va_end( ap );
380 	try {
381 		replxx->print( buf.get(), size );
382 	} catch ( ... ) {
383 		return ( -1 );
384 	}
385 	return ( size );
386 }
387 
replxx_write(::Replxx * replxx_,char const * str,int length)388 int replxx_write( ::Replxx* replxx_, char const* str, int length ) {
389 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
390 	try {
391 		replxx->print( str, length );
392 	} catch ( ... ) {
393 		return ( -1 );
394 	}
395 	return static_cast<int>( length );
396 }
397 
398 struct replxx_completions {
399 	replxx::Replxx::completions_t data;
400 };
401 
402 struct replxx_hints {
403 	replxx::Replxx::hints_t data;
404 };
405 
modify_fwd(replxx_modify_callback_t fn,std::string & line_,int & cursorPosition_,void * userData_)406 void modify_fwd( replxx_modify_callback_t fn, std::string& line_, int& cursorPosition_, void* userData_ ) {
407 #ifdef _WIN32
408 #define strdup _strdup
409 #endif
410 	char* s( strdup( line_.c_str() ) );
411 #undef strdup
412 	fn( &s, &cursorPosition_, userData_ );
413 	line_ = s;
414 	free( s );
415 	return;
416 }
417 
replxx_set_modify_callback(::Replxx * replxx_,replxx_modify_callback_t * fn,void * userData)418 void replxx_set_modify_callback(::Replxx* replxx_, replxx_modify_callback_t* fn, void* userData) {
419 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
420 	replxx->set_modify_callback( std::bind( &modify_fwd, fn, _1, _2, userData ) );
421 }
422 
completions_fwd(replxx_completion_callback_t fn,std::string const & input_,int & contextLen_,void * userData)423 replxx::Replxx::completions_t completions_fwd( replxx_completion_callback_t fn, std::string const& input_, int& contextLen_, void* userData ) {
424 	replxx_completions completions;
425 	fn( input_.c_str(), &completions, &contextLen_, userData );
426 	return ( completions.data );
427 }
428 
429 /* Register a callback function to be called for tab-completion. */
replxx_set_completion_callback(::Replxx * replxx_,replxx_completion_callback_t * fn,void * userData)430 void replxx_set_completion_callback(::Replxx* replxx_, replxx_completion_callback_t* fn, void* userData) {
431 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
432 	replxx->set_completion_callback( std::bind( &completions_fwd, fn, _1, _2, userData ) );
433 }
434 
highlighter_fwd(replxx_highlighter_callback_t fn,std::string const & input,replxx::Replxx::colors_t & colors,void * userData)435 void highlighter_fwd( replxx_highlighter_callback_t fn, std::string const& input, replxx::Replxx::colors_t& colors, void* userData ) {
436 	std::vector<ReplxxColor> colorsTmp( colors.size() );
437 	std::transform(
438 		colors.begin(),
439 		colors.end(),
440 		colorsTmp.begin(),
441 		[]( replxx::Replxx::Color c ) {
442 			return ( static_cast<ReplxxColor>( c ) );
443 		}
444 	);
445 	fn( input.c_str(), colorsTmp.data(), static_cast<int>( colors.size() ), userData );
446 	std::transform(
447 		colorsTmp.begin(),
448 		colorsTmp.end(),
449 		colors.begin(),
450 		[]( ReplxxColor c ) {
451 			return ( static_cast<replxx::Replxx::Color>( c ) );
452 		}
453 	);
454 }
455 
replxx_set_highlighter_callback(::Replxx * replxx_,replxx_highlighter_callback_t * fn,void * userData)456 void replxx_set_highlighter_callback( ::Replxx* replxx_, replxx_highlighter_callback_t* fn, void* userData ) {
457 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
458 	replxx->set_highlighter_callback( std::bind( &highlighter_fwd, fn, _1, _2, userData ) );
459 }
460 
hints_fwd(replxx_hint_callback_t fn,std::string const & input_,int & contextLen_,replxx::Replxx::Color & color_,void * userData)461 replxx::Replxx::hints_t hints_fwd( replxx_hint_callback_t fn, std::string const& input_, int& contextLen_, replxx::Replxx::Color& color_, void* userData ) {
462 	replxx_hints hints;
463 	ReplxxColor c( static_cast<ReplxxColor>( color_ ) );
464 	fn( input_.c_str(), &hints, &contextLen_, &c, userData );
465 	return ( hints.data );
466 }
467 
replxx_set_hint_callback(::Replxx * replxx_,replxx_hint_callback_t * fn,void * userData)468 void replxx_set_hint_callback( ::Replxx* replxx_, replxx_hint_callback_t* fn, void* userData ) {
469 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
470 	replxx->set_hint_callback( std::bind( &hints_fwd, fn, _1, _2, _3, userData ) );
471 }
472 
replxx_add_hint(replxx_hints * lh,const char * str)473 void replxx_add_hint(replxx_hints* lh, const char* str) {
474 	lh->data.emplace_back(str);
475 }
476 
replxx_add_completion(replxx_completions * lc,const char * str)477 void replxx_add_completion( replxx_completions* lc, const char* str ) {
478 	lc->data.emplace_back( str );
479 }
480 
replxx_add_color_completion(replxx_completions * lc,const char * str,ReplxxColor color)481 void replxx_add_color_completion( replxx_completions* lc, const char* str, ReplxxColor color ) {
482 	lc->data.emplace_back( str, static_cast<replxx::Replxx::Color>( color ) );
483 }
484 
replxx_history_add(::Replxx * replxx_,const char * line)485 void replxx_history_add( ::Replxx* replxx_, const char* line ) {
486 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
487 	replxx->history_add( line );
488 }
489 
replxx_set_max_history_size(::Replxx * replxx_,int len)490 void replxx_set_max_history_size( ::Replxx* replxx_, int len ) {
491 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
492 	replxx->set_max_history_size( len );
493 }
494 
replxx_set_max_hint_rows(::Replxx * replxx_,int count)495 void replxx_set_max_hint_rows( ::Replxx* replxx_, int count ) {
496 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
497 	replxx->set_max_hint_rows( count );
498 }
499 
replxx_set_hint_delay(::Replxx * replxx_,int milliseconds)500 void replxx_set_hint_delay( ::Replxx* replxx_, int milliseconds ) {
501 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
502 	replxx->set_hint_delay( milliseconds );
503 }
504 
replxx_set_completion_count_cutoff(::Replxx * replxx_,int count)505 void replxx_set_completion_count_cutoff( ::Replxx* replxx_, int count ) {
506 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
507 	replxx->set_completion_count_cutoff( count );
508 }
509 
replxx_set_word_break_characters(::Replxx * replxx_,char const * breakChars_)510 void replxx_set_word_break_characters( ::Replxx* replxx_, char const* breakChars_ ) {
511 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
512 	replxx->set_word_break_characters( breakChars_ );
513 }
514 
replxx_set_double_tab_completion(::Replxx * replxx_,int val)515 void replxx_set_double_tab_completion( ::Replxx* replxx_, int val ) {
516 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
517 	replxx->set_double_tab_completion( val ? true : false );
518 }
519 
replxx_set_complete_on_empty(::Replxx * replxx_,int val)520 void replxx_set_complete_on_empty( ::Replxx* replxx_, int val ) {
521 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
522 	replxx->set_complete_on_empty( val ? true : false );
523 }
524 
replxx_set_no_color(::Replxx * replxx_,int val)525 void replxx_set_no_color( ::Replxx* replxx_, int val ) {
526 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
527 	replxx->set_no_color( val ? true : false );
528 }
529 
replxx_set_beep_on_ambiguous_completion(::Replxx * replxx_,int val)530 void replxx_set_beep_on_ambiguous_completion( ::Replxx* replxx_, int val ) {
531 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
532 	replxx->set_beep_on_ambiguous_completion( val ? true : false );
533 }
534 
replxx_set_immediate_completion(::Replxx * replxx_,int val)535 void replxx_set_immediate_completion( ::Replxx* replxx_, int val ) {
536 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
537 	replxx->set_immediate_completion( val ? true : false );
538 }
539 
replxx_set_unique_history(::Replxx * replxx_,int val)540 void replxx_set_unique_history( ::Replxx* replxx_, int val ) {
541 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
542 	replxx->set_unique_history( val ? true : false );
543 }
544 
replxx_enable_bracketed_paste(::Replxx * replxx_)545 void replxx_enable_bracketed_paste( ::Replxx* replxx_ ) {
546 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
547 	replxx->enable_bracketed_paste();
548 }
549 
replxx_disable_bracketed_paste(::Replxx * replxx_)550 void replxx_disable_bracketed_paste( ::Replxx* replxx_ ) {
551 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
552 	replxx->disable_bracketed_paste();
553 }
554 
replxx_history_scan_start(::Replxx * replxx_)555 ReplxxHistoryScan* replxx_history_scan_start( ::Replxx* replxx_ ) {
556 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
557 	return ( reinterpret_cast<ReplxxHistoryScan*>( replxx->history_scan().release() ) );
558 }
559 
replxx_history_scan_stop(::Replxx *,ReplxxHistoryScan * historyScan_)560 void replxx_history_scan_stop( ::Replxx*, ReplxxHistoryScan* historyScan_ ) {
561 	delete reinterpret_cast<replxx::Replxx::HistoryScanImpl*>( historyScan_ );
562 }
563 
replxx_history_scan_next(::Replxx *,ReplxxHistoryScan * historyScan_,ReplxxHistoryEntry * historyEntry_)564 int replxx_history_scan_next( ::Replxx*, ReplxxHistoryScan* historyScan_, ReplxxHistoryEntry* historyEntry_ ) {
565 	replxx::Replxx::HistoryScanImpl* historyScan( reinterpret_cast<replxx::Replxx::HistoryScanImpl*>( historyScan_ ) );
566 	bool hasNext( historyScan->next() );
567 	if ( hasNext ) {
568 		replxx::Replxx::HistoryEntry const& historyEntry( historyScan->get() );
569 		historyEntry_->timestamp = historyEntry.timestamp().c_str();
570 		historyEntry_->text = historyEntry.text().c_str();
571 	}
572 	return ( hasNext ? 0 : -1 );
573 }
574 
575 /* Save the history in the specified file. On success 0 is returned
576  * otherwise -1 is returned. */
replxx_history_sync(::Replxx * replxx_,const char * filename)577 int replxx_history_sync( ::Replxx* replxx_, const char* filename ) {
578 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
579 	return ( replxx->history_sync( filename ) ? 0 : -1 );
580 }
581 
582 /* Save the history in the specified file. On success 0 is returned
583  * otherwise -1 is returned. */
replxx_history_save(::Replxx * replxx_,const char * filename)584 int replxx_history_save( ::Replxx* replxx_, const char* filename ) {
585 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
586 	return ( replxx->history_save( filename ) ? 0 : -1 );
587 }
588 
589 /* Load the history from the specified file. If the file does not exist
590  * zero is returned and no operation is performed.
591  *
592  * If the file exists and the operation succeeded 0 is returned, otherwise
593  * on error -1 is returned. */
replxx_history_load(::Replxx * replxx_,const char * filename)594 int replxx_history_load( ::Replxx* replxx_, const char* filename ) {
595 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
596 	return ( replxx->history_load( filename ) ? 0 : -1 );
597 }
598 
replxx_history_clear(::Replxx * replxx_)599 void replxx_history_clear( ::Replxx* replxx_ ) {
600 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
601 	replxx->history_clear();
602 }
603 
replxx_history_size(::Replxx * replxx_)604 int replxx_history_size( ::Replxx* replxx_ ) {
605 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
606 	return ( replxx->history_size() );
607 }
608 
609 /* This special mode is used by replxx in order to print scan codes
610  * on screen for debugging / development purposes. It is implemented
611  * by the replxx-c-api-example program using the --keycodes option. */
612 #ifdef __REPLXX_DEBUG__
replxx_debug_dump_print_codes(void)613 void replxx_debug_dump_print_codes(void) {
614 	char quit[4];
615 
616 	printf(
617 			"replxx key codes debugging mode.\n"
618 			"Press keys to see scan codes. Type 'quit' at any time to exit.\n");
619 	if (enableRawMode() == -1) return;
620 	memset(quit, ' ', 4);
621 	while (1) {
622 		char c;
623 		int nread;
624 
625 #if _WIN32
626 		nread = _read(STDIN_FILENO, &c, 1);
627 #else
628 		nread = read(STDIN_FILENO, &c, 1);
629 #endif
630 		if (nread <= 0) continue;
631 		memmove(quit, quit + 1, sizeof(quit) - 1); /* shift string to left. */
632 		quit[sizeof(quit) - 1] = c; /* Insert current char on the right. */
633 		if (memcmp(quit, "quit", sizeof(quit)) == 0) break;
634 
635 		printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c,
636 					 (int)c);
637 		printf("\r"); /* Go left edge manually, we are in raw mode. */
638 		fflush(stdout);
639 	}
640 	disableRawMode();
641 }
642 #endif // __REPLXX_DEBUG__
643 
replxx_install_window_change_handler(::Replxx * replxx_)644 int replxx_install_window_change_handler( ::Replxx* replxx_ ) {
645 	replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
646 	return ( replxx->install_window_change_handler() );
647 }
648 
649