Browse Source

[Rework] Replace linenoise with replxx

Source: https://github.com/AmokHuginnsson/replxx
tags/2.0
Vsevolod Stakhov 4 years ago
parent
commit
669cd52f68

+ 0
- 10
contrib/linenoise/CMakeLists.txt View File

@@ -1,10 +0,0 @@
SET(LINENOISESRC linenoise.c)

ADD_LIBRARY(rspamd-linenoise STATIC ${LINENOISESRC})
SET_TARGET_PROPERTIES(rspamd-linenoise PROPERTIES VERSION ${RSPAMD_VERSION})

IF(ENABLE_FULL_DEBUG MATCHES "OFF")
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
SET_TARGET_PROPERTIES(rspamd-linenoise PROPERTIES COMPILE_FLAGS "-O3")
endif ()
ENDIF()

+ 0
- 25
contrib/linenoise/LICENSE View File

@@ -1,25 +0,0 @@
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0
- 1194
contrib/linenoise/linenoise.c
File diff suppressed because it is too large
View File


+ 0
- 76
contrib/linenoise/linenoise.h View File

@@ -1,76 +0,0 @@
/* linenoise.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef __LINENOISE_H
#define __LINENOISE_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;

typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);

char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);

/* Rspamd specific */
int linenoiseGetColumns(int ifd, int ofd);

#ifdef __cplusplus
}
#endif

#endif /* __LINENOISE_H */

+ 82
- 0
contrib/replxx/CMakeLists.txt View File

@@ -0,0 +1,82 @@
# -*- mode: CMAKE; -*-

cmake_minimum_required(VERSION 3.0)

project( replxx VERSION 0.0.2 LANGUAGES CXX C )
message(STATUS "Build mode: ${CMAKE_BUILD_TYPE}")

# INFO
set(REPLXX_DISPLAY_NAME "replxx")
set(REPLXX_URL_INFO_ABOUT "https://github.com/AmokHuginnsson/replxx")
set(REPLXX_CONTACT "amok@codestation.org")
set(REPLXX_FRIENDLY_STRING "replxx - Read Evaluate Print Loop library")

# compiler options
if(CMAKE_COMPILER_IS_GNUCXX)
message(STATUS "Compiler type GNU: ${CMAKE_CXX_COMPILER}")
set(BASE_COMPILER_OPTIONS "-std=c++11 -Wall -D_GNU_SOURCE -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS} -O0 --coverage -fno-inline -fno-default-inline -fno-inline-small-functions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g -ggdb -g3 -ggdb3")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${BASE_COMPILER_OPTIONS} -Os")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${BASE_COMPILER_OPTIONS} -O3 -g")
set(CMAKE_C_FLAGS "-std=c99")
elseif(CMAKE_COMPILER_IS_CLANGCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# using regular Clang or AppleClang
message(STATUS "Compiler type CLANG: ${CMAKE_CXX_COMPILER}")
set(BASE_COMPILER_OPTIONS "-std=c++11 -Wall -Wextra -D_GNU_SOURCE -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${BASE_COMPILER_OPTIONS} -Os")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${BASE_COMPILER_OPTIONS} -O3 -g")
set(CMAKE_C_FLAGS "-std=c99")
elseif(MSVC)
message(STATUS "Compiler type MSVC: ${CMAKE_CXX_COMPILER}")
add_definitions("-D_CRT_SECURE_NO_WARNINGS=1")

set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO /SUBSYSTEM:CONSOLE /LTCG /ignore:4099")
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /SUBSYSTEM:CONSOLE /ignore:4099")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SUBSYSTEM:CONSOLE /ignore:4099")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /SUBSYSTEM:CONSOLE /ignore:4099")
else()
# unknown compiler
message(STATUS "Compiler type UNKNOWN: ${CMAKE_CXX_COMPILER}")
set(BASE_COMPILER_OPTIONS "-std=c++11 -Wall -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${BASE_COMPILER_OPTIONS} -Os")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${BASE_COMPILER_OPTIONS} -O3 -g")
set(CMAKE_C_FLAGS "-std=c99")
endif()

# build libreplxx
set(
REPLXX_SOURCES
src/conversion.cxx
src/ConvertUTF.cpp
src/escape.cxx
src/history.cxx
src/replxx_impl.cxx
src/io.cxx
src/prompt.cxx
src/replxx.cxx
src/util.cxx
src/wcwidth.cpp
src/windows.cxx
)

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
add_library(rspamd-replxx SHARED ${REPLXX_SOURCES})

target_include_directories(
rspamd-replxx
PUBLIC ${PROJECT_SOURCE_DIR}/include
PRIVATE ${PROJECT_SOURCE_DIR}/src
)
set( TARGETS ${TARGETS} rspamd-replxx )
target_compile_definitions(rspamd-replxx PRIVATE REPLXX_BUILDING_DLL)

install( TARGETS ${TARGETS} LIBRARY DESTINATION ${RSPAMD_LIBDIR})

+ 63
- 0
contrib/replxx/LICENSE.md View File

@@ -0,0 +1,63 @@
Copyright (c) 2017-2018, Marcin Konarski (amok at codestation.org)
Copyright (c) 2010, Salvatore Sanfilippo (antirez at gmail dot com)
Copyright (c) 2010, Pieter Noordhuis (pcnoordhuis at gmail dot com)

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.


wcwidth.cpp
===========

Markus Kuhn -- 2007-05-26 (Unicode 5.0)

Permission to use, copy, modify, and distribute this software
for any purpose and without fee is hereby granted. The author
disclaims all warranties with regard to this software.


ConvertUTF.cpp
==============

Copyright 2001-2004 Unicode, Inc.

Disclaimer

This source code is provided as is by Unicode, Inc. No claims are
made as to fitness for any particular purpose. No warranties of any
kind are expressed or implied. The recipient agrees to determine
applicability of information provided. If this file has been
purchased on magnetic or optical media from Unicode, Inc., the
sole remedy for any claim will be exchange of defective media
within 90 days of receipt.

Limitations on Rights to Redistribute This Code

Unicode, Inc. hereby grants the right to freely use the information
supplied in this file in the creation of products supporting the
Unicode Standard, and to make copies of this file in any form
for internal or external distribution as long as this notice
remains attached.

+ 119
- 0
contrib/replxx/README.md View File

@@ -0,0 +1,119 @@
# Read Evaluate Print Loop ++

![demo](https://drive.google.com/uc?export=download&id=0B53g2Y3z7rWNT2dCRGVVNldaRnc)

[![Build Status](https://travis-ci.org/AmokHuginnsson/replxx.svg?branch=master)](https://travis-ci.org/AmokHuginnsson/replxx)

A small, portable GNU readline replacement for Linux, Windows and
MacOS which is capable of handling UTF-8 characters. Unlike GNU
readline, which is GPL, this library uses a BSD license and can be
used in any kind of program.

## Origin

This replxx implementation is based on the work by
[ArangoDB Team](https://github.com/arangodb/linenoise-ng) and
[Salvatore Sanfilippo](https://github.com/antirez/linenoise) and
10gen Inc. The goal is to create a zero-config, BSD
licensed, readline replacement usable in Apache2 or BSD licensed
programs.

## Features

* single-line and multi-line editing mode with the usual key bindings implemented
* history handling
* completion
* syntax highlighting
* hints
* BSD license source code
* Only uses a subset of VT100 escapes (ANSI.SYS compatible)
* UTF8 aware
* support for Linux, MacOS and Windows

It deviates from Salvatore's original goal to have a minimal readline
replacement for the sake of supporting UTF8 and Windows. It deviates
from 10gen Inc.'s goal to create a C++ interface to linenoise. This
library uses C++ internally, but to the user it provides a pure C
interface that is compatible with the original linenoise API.
C interface.

## Requirements

To build this library, you will need a C++11-enabled compiler and
some recent version of CMake.

## Build instructions

### *nix

1. Create a build directory

```bash
mkdir -p build && cd build
```

2. Build the library

```bash
cmake -DCMAKE_BUILD_TYPE=Release .. && make
```

3. Install the library at the default target location

```bash
sudo make install
```

The default installation location can be adjusted by setting the `DESTDIR`
variable when invoking `make install`:

```bash
make DESTDIR=/tmp install
```

### Windows

1. Create a build directory in MS-DOS command prompt

```
md build
cd build
```

2. Generate Visual Studio solution file with cmake

* 32 bit:
```bash
cmake -G "Visual Studio 12 2013" -DCMAKE_BUILD_TYPE=Release ..`
```
* 64 bit:
```bash
`cmake -G "Visual Studio 12 2013 Win64" -DCMAKE_BUILD_TYPE=Release ..`
```

3. Open the generated file `replxx.sln` in the `build` subdirectory with Visual Studio.

## Tested with...

* Linux text only console ($TERM = linux)
* Linux KDE terminal application ($TERM = xterm)
* Linux xterm ($TERM = xterm)
* Linux Buildroot ($TERM = vt100)
* Mac OS X iTerm ($TERM = xterm)
* Mac OS X default Terminal.app ($TERM = xterm)
* OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
* IBM AIX 6.1
* FreeBSD xterm ($TERM = xterm)
* ANSI.SYS
* Emacs comint mode ($TERM = dumb)
* Windows

Please test it everywhere you can and report back!

## Let's push this forward!

Patches should be provided in the respect of linenoise sensibility for
small and easy to understand code that and the license
restrictions. Extensions must be submitted under a BSD license-style.
A contributor license is required for contributions.


+ 454
- 0
contrib/replxx/include/replxx.h View File

@@ -0,0 +1,454 @@
/* linenoise.h -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef __REPLXX_H
#define __REPLXX_H

#define REPLXX_VERSION "0.0.2"
#define REPLXX_VERSION_MAJOR 0
#define REPLXX_VERSION_MINOR 0

#ifdef __cplusplus
extern "C" {
#endif

/*
* For use in Windows DLLs:
*
* If you are building replxx into a DLL,
* unless you are using supplied CMake based build,
* ensure that 'REPLXX_BUILDING_DLL' is defined when
* building the DLL so that proper symbols are exported.
*/
#if defined( _WIN32 ) && ! defined( REPLXX_STATIC )
# ifdef REPLXX_BUILDING_DLL
# define REPLXX_IMPEXP __declspec( dllexport )
# else
# define REPLXX_IMPEXP __declspec( dllimport )
# endif
#else
# define REPLXX_IMPEXP /**/
#endif

/*! \brief Color definitions to use in highlighter callbacks.
*/
typedef enum {
REPLXX_COLOR_BLACK = 0,
REPLXX_COLOR_RED = 1,
REPLXX_COLOR_GREEN = 2,
REPLXX_COLOR_BROWN = 3,
REPLXX_COLOR_BLUE = 4,
REPLXX_COLOR_MAGENTA = 5,
REPLXX_COLOR_CYAN = 6,
REPLXX_COLOR_LIGHTGRAY = 7,
REPLXX_COLOR_GRAY = 8,
REPLXX_COLOR_BRIGHTRED = 9,
REPLXX_COLOR_BRIGHTGREEN = 10,
REPLXX_COLOR_YELLOW = 11,
REPLXX_COLOR_BRIGHTBLUE = 12,
REPLXX_COLOR_BRIGHTMAGENTA = 13,
REPLXX_COLOR_BRIGHTCYAN = 14,
REPLXX_COLOR_WHITE = 15,
REPLXX_COLOR_NORMAL = REPLXX_COLOR_LIGHTGRAY,
REPLXX_COLOR_DEFAULT = -1,
REPLXX_COLOR_ERROR = -2
} ReplxxColor;

enum { REPLXX_KEY_BASE = 0x0010ffff + 1 };
enum { REPLXX_KEY_BASE_SHIFT = 0x01000000 };
enum { REPLXX_KEY_BASE_CONTROL = 0x02000000 };
enum { REPLXX_KEY_BASE_META = 0x04000000 };
enum { REPLXX_KEY_ESCAPE = 27 };
enum { REPLXX_KEY_PAGE_UP = REPLXX_KEY_BASE + 1 };
enum { REPLXX_KEY_PAGE_DOWN = REPLXX_KEY_PAGE_UP + 1 };
enum { REPLXX_KEY_DOWN = REPLXX_KEY_PAGE_DOWN + 1 };
enum { REPLXX_KEY_UP = REPLXX_KEY_DOWN + 1 };
enum { REPLXX_KEY_LEFT = REPLXX_KEY_UP + 1 };
enum { REPLXX_KEY_RIGHT = REPLXX_KEY_LEFT + 1 };
enum { REPLXX_KEY_HOME = REPLXX_KEY_RIGHT + 1 };
enum { REPLXX_KEY_END = REPLXX_KEY_HOME + 1 };
enum { REPLXX_KEY_DELETE = REPLXX_KEY_END + 1 };
enum { REPLXX_KEY_INSERT = REPLXX_KEY_DELETE + 1 };
enum { REPLXX_KEY_F1 = REPLXX_KEY_INSERT + 1 };
enum { REPLXX_KEY_F2 = REPLXX_KEY_F1 + 1 };
enum { REPLXX_KEY_F3 = REPLXX_KEY_F2 + 1 };
enum { REPLXX_KEY_F4 = REPLXX_KEY_F3 + 1 };
enum { REPLXX_KEY_F5 = REPLXX_KEY_F4 + 1 };
enum { REPLXX_KEY_F6 = REPLXX_KEY_F5 + 1 };
enum { REPLXX_KEY_F7 = REPLXX_KEY_F6 + 1 };
enum { REPLXX_KEY_F8 = REPLXX_KEY_F7 + 1 };
enum { REPLXX_KEY_F9 = REPLXX_KEY_F8 + 1 };
enum { REPLXX_KEY_F10 = REPLXX_KEY_F9 + 1 };
enum { REPLXX_KEY_F11 = REPLXX_KEY_F10 + 1 };
enum { REPLXX_KEY_F12 = REPLXX_KEY_F11 + 1 };
enum { REPLXX_KEY_F13 = REPLXX_KEY_F12 + 1 };
enum { REPLXX_KEY_F14 = REPLXX_KEY_F13 + 1 };
enum { REPLXX_KEY_F15 = REPLXX_KEY_F14 + 1 };
enum { REPLXX_KEY_F16 = REPLXX_KEY_F15 + 1 };
enum { REPLXX_KEY_F17 = REPLXX_KEY_F16 + 1 };
enum { REPLXX_KEY_F18 = REPLXX_KEY_F17 + 1 };
enum { REPLXX_KEY_F19 = REPLXX_KEY_F18 + 1 };
enum { REPLXX_KEY_F20 = REPLXX_KEY_F19 + 1 };
enum { REPLXX_KEY_F21 = REPLXX_KEY_F20 + 1 };
enum { REPLXX_KEY_F22 = REPLXX_KEY_F21 + 1 };
enum { REPLXX_KEY_F23 = REPLXX_KEY_F22 + 1 };
enum { REPLXX_KEY_F24 = REPLXX_KEY_F23 + 1 };
enum { REPLXX_KEY_MOUSE = REPLXX_KEY_F24 + 1 };

#define REPLXX_KEY_SHIFT( key ) ( ( key ) | REPLXX_KEY_BASE_SHIFT )
#define REPLXX_KEY_CONTROL( key ) ( ( key ) | REPLXX_KEY_BASE_CONTROL )
#define REPLXX_KEY_META( key ) ( ( key ) | REPLXX_KEY_BASE_META )

enum { REPLXX_KEY_BACKSPACE = REPLXX_KEY_CONTROL( 'H' ) };
enum { REPLXX_KEY_TAB = REPLXX_KEY_CONTROL( 'I' ) };
enum { REPLXX_KEY_ENTER = REPLXX_KEY_CONTROL( 'M' ) };

/*! \brief List of built-in actions that act upon user input.
*/
typedef enum {
REPLXX_ACTION_INSERT_CHARACTER,
REPLXX_ACTION_DELETE_CHARACTER_UNDER_CURSOR,
REPLXX_ACTION_DELETE_CHARACTER_LEFT_OF_CURSOR,
REPLXX_ACTION_KILL_TO_END_OF_LINE,
REPLXX_ACTION_KILL_TO_BEGINING_OF_LINE,
REPLXX_ACTION_KILL_TO_END_OF_WORD,
REPLXX_ACTION_KILL_TO_BEGINING_OF_WORD,
REPLXX_ACTION_KILL_TO_WHITESPACE_ON_LEFT,
REPLXX_ACTION_YANK,
REPLXX_ACTION_YANK_CYCLE,
REPLXX_ACTION_MOVE_CURSOR_TO_BEGINING_OF_LINE,
REPLXX_ACTION_MOVE_CURSOR_TO_END_OF_LINE,
REPLXX_ACTION_MOVE_CURSOR_ONE_WORD_LEFT,
REPLXX_ACTION_MOVE_CURSOR_ONE_WORD_RIGHT,
REPLXX_ACTION_MOVE_CURSOR_LEFT,
REPLXX_ACTION_MOVE_CURSOR_RIGHT,
REPLXX_ACTION_HISTORY_NEXT,
REPLXX_ACTION_HISTORY_PREVIOUS,
REPLXX_ACTION_HISTORY_FIRST,
REPLXX_ACTION_HISTORY_LAST,
REPLXX_ACTION_HISTORY_INCREMENTAL_SEARCH,
REPLXX_ACTION_HISTORY_COMMON_PREFIX_SEARCH,
REPLXX_ACTION_HINT_NEXT,
REPLXX_ACTION_HINT_PREVIOUS,
REPLXX_ACTION_CAPITALIZE_WORD,
REPLXX_ACTION_LOWERCASE_WORD,
REPLXX_ACTION_UPPERCASE_WORD,
REPLXX_ACTION_TRANSPOSE_CHARACTERS,
REPLXX_ACTION_TOGGLE_OVERWRITE_MODE,
#ifndef _WIN32
REPLXX_ACTION_VERBATIM_INSERT,
REPLXX_ACTION_SUSPEND,
#endif
REPLXX_ACTION_CLEAR_SCREEN,
REPLXX_ACTION_CLEAR_SELF,
REPLXX_ACTION_REPAINT,
REPLXX_ACTION_COMPLETE_LINE,
REPLXX_ACTION_COMPLETE_NEXT,
REPLXX_ACTION_COMPLETE_PREVIOUS,
REPLXX_ACTION_COMMIT_LINE,
REPLXX_ACTION_ABORT_LINE,
REPLXX_ACTION_SEND_EOF
} ReplxxAction;

/*! \brief Possible results of key-press handler actions.
*/
typedef enum {
REPLXX_ACTION_RESULT_CONTINUE, /*!< Continue processing user input. */
REPLXX_ACTION_RESULT_RETURN, /*!< Return user input entered so far. */
REPLXX_ACTION_RESULT_BAIL /*!< Stop processing user input, returns nullptr from the \e input() call. */
} ReplxxActionResult;

typedef struct ReplxxStateTag {
char const* text;
int cursorPosition;
} ReplxxState;

typedef struct Replxx Replxx;

/*! \brief Create Replxx library resouce holder.
*
* Use replxx_end() to free resoiurce acquired with this function.
*
* \return Replxx library resouce holder.
*/
REPLXX_IMPEXP Replxx* replxx_init( void );

/*! \brief Cleanup resources used by Replxx library.
*
* \param replxx - a Replxx library resource holder.
*/
REPLXX_IMPEXP void replxx_end( Replxx* replxx );

/*! \brief Highlighter callback type definition.
*
* If user want to have colorful input she must simply install highlighter callback.
* The callback would be invoked by the library after each change to the input done by
* the user. After callback returns library uses data from colors buffer to colorize
* displayed user input.
*
* \e size of \e colors buffer is equal to number of code points in user \e input
* which will be different from simple `strlen( input )`!
*
* \param input - an UTF-8 encoded input entered by the user so far.
* \param colors - output buffer for color information.
* \param size - size of output buffer for color information.
* \param userData - pointer to opaque user data block.
*/
typedef void (replxx_highlighter_callback_t)(char const* input, ReplxxColor* colors, int size, void* userData);

/*! \brief Register highlighter callback.
*
* \param fn - user defined callback function.
* \param userData - pointer to opaque user data block to be passed into each invocation of the callback.
*/
REPLXX_IMPEXP void replxx_set_highlighter_callback( Replxx*, replxx_highlighter_callback_t* fn, void* userData );

typedef struct replxx_completions replxx_completions;

/*! \brief Completions callback type definition.
*
* \e contextLen is counted in Unicode code points (not in bytes!).
*
* For user input:
* if ( obj.me
*
* input == "if ( obj.me"
* contextLen == 2 (depending on \e replxx_set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
* \param completions - pointer to opaque list of user completions.
* \param contextLen[in,out] - length of the additional context to provide while displaying completions.
* \param userData - pointer to opaque user data block.
*/
typedef void(replxx_completion_callback_t)(const char* input, replxx_completions* completions, int* contextLen, void* userData);

/*! \brief Register completion callback.
*
* \param fn - user defined callback function.
* \param userData - pointer to opaque user data block to be passed into each invocation of the callback.
*/
REPLXX_IMPEXP void replxx_set_completion_callback( Replxx*, replxx_completion_callback_t* fn, void* userData );

/*! \brief Add another possible completion for current user input.
*
* \param completions - pointer to opaque list of user completions.
* \param str - UTF-8 encoded completion string.
*/
REPLXX_IMPEXP void replxx_add_completion( replxx_completions* completions, const char* str );

/*! \brief Add another possible completion for current user input.
*
* \param completions - pointer to opaque list of user completions.
* \param str - UTF-8 encoded completion string.
* \param color - a color for the completion.
*/
REPLXX_IMPEXP void replxx_add_color_completion( replxx_completions* completions, const char* str, ReplxxColor color );

typedef struct replxx_hints replxx_hints;

/*! \brief Hints callback type definition.
*
* \e contextLen is counted in Unicode code points (not in bytes!).
*
* For user input:
* if ( obj.me
*
* input == "if ( obj.me"
* contextLen == 2 (depending on \e replxx_set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
* \param hints - pointer to opaque list of possible hints.
* \param contextLen[in,out] - length of the additional context to provide while displaying hints.
* \param color - a color used for displaying hints.
* \param userData - pointer to opaque user data block.
*/
typedef void(replxx_hint_callback_t)(const char* input, replxx_hints* hints, int* contextLen, ReplxxColor* color, void* userData);

/*! \brief Register hints callback.
*
* \param fn - user defined callback function.
* \param userData - pointer to opaque user data block to be passed into each invocation of the callback.
*/
REPLXX_IMPEXP void replxx_set_hint_callback( Replxx*, replxx_hint_callback_t* fn, void* userData );

/*! \brief Key press handler type definition.
*
* \param code - the key code replxx got from terminal.
* \return Decition on how should input() behave after this key handler returns.
*/
typedef ReplxxActionResult (key_press_handler_t)( int code, void* userData );

/*! \brief Add another possible hint for current user input.
*
* \param hints - pointer to opaque list of hints.
* \param str - UTF-8 encoded hint string.
*/
REPLXX_IMPEXP void replxx_add_hint( replxx_hints* hints, const char* str );

/*! \brief Read line of user input.
*
* \param prompt - prompt to be displayed before getting user input.
* \return An UTF-8 encoded input given by the user (or nullptr on EOF).
*/
REPLXX_IMPEXP char const* replxx_input( Replxx*, const char* prompt );

/*! \brief Get current state data.
*
* This call is intended to be used in handlers.
*
* \param state - buffer for current state of the model.
*/
REPLXX_IMPEXP void replxx_get_state( Replxx*, ReplxxState* state );

/*! \brief Set new state data.
*
* This call is intended to be used in handlers.
*
* \param state - new state of the model.
*/
REPLXX_IMPEXP void replxx_set_state( Replxx*, ReplxxState* state );

/*! \brief Print formatted string to standard output.
*
* This function ensures proper handling of ANSI escape sequences
* contained in printed data, which is especially useful on Windows
* since Unixes handle them correctly out of the box.
*
* \param fmt - printf style format.
*/
REPLXX_IMPEXP int replxx_print( Replxx*, char const* fmt, ... );

/*! \brief Schedule an emulated key press event.
*
* \param code - key press code to be emulated.
*/
REPLXX_IMPEXP void replxx_emulate_key_press( Replxx*, int unsigned code );

/*! \brief Invoke built-in action handler.
*
* \pre This function can be called only from key-press handler.
*
* \param action - a built-in action to invoke.
* \param code - a supplementary key-code to consume by built-in action handler.
* \return The action result informing the replxx what shall happen next.
*/
REPLXX_IMPEXP ReplxxActionResult replxx_invoke( Replxx*, ReplxxAction action, int unsigned code );

/*! \brief Bind user defined action to handle given key-press event.
*
* \param code - handle this key-press event with following handler.
* \param handler - use this handler to handle key-press event.
* \param userData - supplementary user data passed to invoked handlers.
*/
REPLXX_IMPEXP void replxx_bind_key( Replxx*, int code, key_press_handler_t handler, void* userData );

REPLXX_IMPEXP void replxx_set_preload_buffer( Replxx*, const char* preloadText );

REPLXX_IMPEXP void replxx_history_add( Replxx*, const char* line );
REPLXX_IMPEXP int replxx_history_size( Replxx* );

/*! \brief Set set of word break characters.
*
* This setting influences word based cursor movement and line editing capabilities.
*
* \param wordBreakers - 7-bit ASCII set of word breaking characters.
*/
REPLXX_IMPEXP void replxx_set_word_break_characters( Replxx*, char const* wordBreakers );

/*! \brief How many completions should trigger pagination.
*/
REPLXX_IMPEXP void replxx_set_completion_count_cutoff( Replxx*, int count );

/*! \brief Set maximum number of displayed hint rows.
*/
REPLXX_IMPEXP void replxx_set_max_hint_rows( Replxx*, int count );

/*! \brief Set a delay before hint are shown after user stopped typing..
*
* \param milliseconds - a number of milliseconds to wait before showing hints.
*/
REPLXX_IMPEXP void replxx_set_hint_delay( Replxx*, int milliseconds );

/*! \brief Set tab completion behavior.
*
* \param val - use double tab to invoke completions (if != 0).
*/
REPLXX_IMPEXP void replxx_set_double_tab_completion( Replxx*, int val );

/*! \brief Set tab completion behavior.
*
* \param val - invoke completion even if user input is empty (if != 0).
*/
REPLXX_IMPEXP void replxx_set_complete_on_empty( Replxx*, int val );

/*! \brief Set tab completion behavior.
*
* \param val - beep if completion is ambiguous (if != 0).
*/
REPLXX_IMPEXP void replxx_set_beep_on_ambiguous_completion( Replxx*, int val );

/*! \brief Disable output coloring.
*
* \param val - if set to non-zero disable output colors.
*/
REPLXX_IMPEXP void replxx_set_no_color( Replxx*, int val );

/*! \brief Set maximum number of entries in history list.
*/
REPLXX_IMPEXP void replxx_set_max_history_size( Replxx*, int len );
REPLXX_IMPEXP char const* replxx_history_line( Replxx*, int index );
REPLXX_IMPEXP int replxx_history_save( Replxx*, const char* filename );
REPLXX_IMPEXP int replxx_history_load( Replxx*, const char* filename );
REPLXX_IMPEXP void replxx_clear_screen( Replxx* );
#ifdef __REPLXX_DEBUG__
void replxx_debug_dump_print_codes(void);
#endif
/* the following is extension to the original linenoise API */
REPLXX_IMPEXP int replxx_install_window_change_handler( Replxx* );

#ifdef __cplusplus
}
#endif

#endif /* __REPLXX_H */


+ 466
- 0
contrib/replxx/include/replxx.hxx View File

@@ -0,0 +1,466 @@
/*
* Copyright (c) 2017-2018, Marcin Konarski (amok at codestation.org)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef HAVE_REPLXX_HXX_INCLUDED
#define HAVE_REPLXX_HXX_INCLUDED 1

#include <memory>
#include <vector>
#include <string>
#include <functional>

/*
* For use in Windows DLLs:
*
* If you are building replxx into a DLL,
* unless you are using supplied CMake based build,
* ensure that 'REPLXX_BUILDING_DLL' is defined when
* building the DLL so that proper symbols are exported.
*/
#if defined( _WIN32 ) && ! defined( REPLXX_STATIC )
# ifdef REPLXX_BUILDING_DLL
# define REPLXX_IMPEXP __declspec( dllexport )
# else
# define REPLXX_IMPEXP __declspec( dllimport )
# endif
#else
# define REPLXX_IMPEXP /**/
#endif

#ifdef ERROR
enum { ERROR_BB1CA97EC761FC37101737BA0AA2E7C5 = ERROR };
#undef ERROR
enum { ERROR = ERROR_BB1CA97EC761FC37101737BA0AA2E7C5 };
#endif
#ifdef DELETE
enum { DELETE_32F68A60CEF40FAEDBC6AF20298C1A1E = DELETE };
#undef DELETE
enum { DELETE = DELETE_32F68A60CEF40FAEDBC6AF20298C1A1E };
#endif

namespace replxx {

class REPLXX_IMPEXP Replxx {
public:
enum class Color {
BLACK = 0,
RED = 1,
GREEN = 2,
BROWN = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
LIGHTGRAY = 7,
GRAY = 8,
BRIGHTRED = 9,
BRIGHTGREEN = 10,
YELLOW = 11,
BRIGHTBLUE = 12,
BRIGHTMAGENTA = 13,
BRIGHTCYAN = 14,
WHITE = 15,
NORMAL = LIGHTGRAY,
DEFAULT = -1,
ERROR = -2
};
struct KEY {
static char32_t const BASE = 0x0010ffff + 1;
static char32_t const BASE_SHIFT = 0x01000000;
static char32_t const BASE_CONTROL = 0x02000000;
static char32_t const BASE_META = 0x04000000;
static char32_t const ESCAPE = 27;
static char32_t const PAGE_UP = BASE + 1;
static char32_t const PAGE_DOWN = PAGE_UP + 1;
static char32_t const DOWN = PAGE_DOWN + 1;
static char32_t const UP = DOWN + 1;
static char32_t const LEFT = UP + 1;
static char32_t const RIGHT = LEFT + 1;
static char32_t const HOME = RIGHT + 1;
static char32_t const END = HOME + 1;
static char32_t const DELETE = END + 1;
static char32_t const INSERT = DELETE + 1;
static char32_t const F1 = INSERT + 1;
static char32_t const F2 = F1 + 1;
static char32_t const F3 = F2 + 1;
static char32_t const F4 = F3 + 1;
static char32_t const F5 = F4 + 1;
static char32_t const F6 = F5 + 1;
static char32_t const F7 = F6 + 1;
static char32_t const F8 = F7 + 1;
static char32_t const F9 = F8 + 1;
static char32_t const F10 = F9 + 1;
static char32_t const F11 = F10 + 1;
static char32_t const F12 = F11 + 1;
static char32_t const F13 = F12 + 1;
static char32_t const F14 = F13 + 1;
static char32_t const F15 = F14 + 1;
static char32_t const F16 = F15 + 1;
static char32_t const F17 = F16 + 1;
static char32_t const F18 = F17 + 1;
static char32_t const F19 = F18 + 1;
static char32_t const F20 = F19 + 1;
static char32_t const F21 = F20 + 1;
static char32_t const F22 = F21 + 1;
static char32_t const F23 = F22 + 1;
static char32_t const F24 = F23 + 1;
static char32_t const MOUSE = F24 + 1;
static constexpr char32_t shift( char32_t key_ ) {
return ( key_ | BASE_SHIFT );
}
static constexpr char32_t control( char32_t key_ ) {
return ( key_ | BASE_CONTROL );
}
static constexpr char32_t meta( char32_t key_ ) {
return ( key_ | BASE_META );
}
static char32_t const BACKSPACE = 'H' | BASE_CONTROL;
static char32_t const TAB = 'I' | BASE_CONTROL;
static char32_t const ENTER = 'M' | BASE_CONTROL;
};
/*! \brief List of built-in actions that act upon user input.
*/
enum class ACTION {
INSERT_CHARACTER,
DELETE_CHARACTER_UNDER_CURSOR,
DELETE_CHARACTER_LEFT_OF_CURSOR,
KILL_TO_END_OF_LINE,
KILL_TO_BEGINING_OF_LINE,
KILL_TO_END_OF_WORD,
KILL_TO_BEGINING_OF_WORD,
KILL_TO_WHITESPACE_ON_LEFT,
YANK,
YANK_CYCLE,
MOVE_CURSOR_TO_BEGINING_OF_LINE,
MOVE_CURSOR_TO_END_OF_LINE,
MOVE_CURSOR_ONE_WORD_LEFT,
MOVE_CURSOR_ONE_WORD_RIGHT,
MOVE_CURSOR_LEFT,
MOVE_CURSOR_RIGHT,
HISTORY_NEXT,
HISTORY_PREVIOUS,
HISTORY_FIRST,
HISTORY_LAST,
HISTORY_INCREMENTAL_SEARCH,
HISTORY_COMMON_PREFIX_SEARCH,
HINT_NEXT,
HINT_PREVIOUS,
CAPITALIZE_WORD,
LOWERCASE_WORD,
UPPERCASE_WORD,
TRANSPOSE_CHARACTERS,
TOGGLE_OVERWRITE_MODE,
#ifndef _WIN32
VERBATIM_INSERT,
SUSPEND,
#endif
CLEAR_SCREEN,
CLEAR_SELF,
REPAINT,
COMPLETE_LINE,
COMPLETE_NEXT,
COMPLETE_PREVIOUS,
COMMIT_LINE,
ABORT_LINE,
SEND_EOF
};
/*! \brief Possible results of key-press handler actions.
*/
enum class ACTION_RESULT {
CONTINUE, /*!< Continue processing user input. */
RETURN, /*!< Return user input entered so far. */
BAIL /*!< Stop processing user input, returns nullptr from the \e input() call. */
};
typedef std::vector<Color> colors_t;
class Completion {
std::string _text;
Color _color;
public:
Completion( char const* text_ )
: _text( text_ )
, _color( Color::DEFAULT ) {
}
Completion( std::string const& text_ )
: _text( text_ )
, _color( Color::DEFAULT ) {
}
Completion( std::string const& text_, Color color_ )
: _text( text_ )
, _color( color_ ) {
}
std::string const& text( void ) const {
return ( _text );
}
Color color( void ) const {
return ( _color );
}
};
typedef std::vector<Completion> completions_t;
typedef std::vector<std::string> hints_t;

/*! \brief Completions callback type definition.
*
* \e contextLen is counted in Unicode code points (not in bytes!).
*
* For user input:
* if ( obj.me
*
* input == "if ( obj.me"
* contextLen == 2 (depending on \e set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
* \param[in,out] contextLen - length of the additional context to provide while displaying completions.
* \return A list of user completions.
*/
typedef std::function<completions_t ( std::string const& input, int& contextLen )> completion_callback_t;

/*! \brief Highlighter callback type definition.
*
* If user want to have colorful input she must simply install highlighter callback.
* The callback would be invoked by the library after each change to the input done by
* the user. After callback returns library uses data from colors buffer to colorize
* displayed user input.
*
* Size of \e colors buffer is equal to number of code points in user \e input
* which will be different from simple `input.lenght()`!
*
* \param input - an UTF-8 encoded input entered by the user so far.
* \param colors - output buffer for color information.
*/
typedef std::function<void ( std::string const& input, colors_t& colors )> highlighter_callback_t;

/*! \brief Hints callback type definition.
*
* \e contextLen is counted in Unicode code points (not in bytes!).
*
* For user input:
* if ( obj.me
*
* input == "if ( obj.me"
* contextLen == 2 (depending on \e set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
* \param contextLen[in,out] - length of the additional context to provide while displaying hints.
* \param color - a color used for displaying hints.
* \return A list of possible hints.
*/
typedef std::function<hints_t ( std::string const& input, int& contextLen, Color& color )> hint_callback_t;

/*! \brief Key press handler type definition.
*
* \param code - the key code replxx got from terminal.
* \return Decition on how should input() behave after this key handler returns.
*/
typedef std::function<ACTION_RESULT ( char32_t code )> key_press_handler_t;

struct State {
char const* _text;
int _cursorPosition;
State( char const* text_, int cursorPosition_ = -1 )
: _text( text_ )
, _cursorPosition( cursorPosition_ ) {
}
State( State const& ) = default;
State& operator = ( State const& ) = default;
char const* text( void ) const {
return ( _text );
}
int cursor_position( void ) const {
return ( _cursorPosition );
}
};

class ReplxxImpl;
private:
typedef std::unique_ptr<ReplxxImpl, void (*)( ReplxxImpl* )> impl_t;
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable:4251)
#endif
impl_t _impl;
#ifdef _WIN32
#pragma warning(pop)
#endif

public:
Replxx( void );
Replxx( Replxx&& ) = default;
Replxx& operator = ( Replxx&& ) = default;

/*! \brief Register completion callback.
*
* \param fn - user defined callback function.
*/
void set_completion_callback( completion_callback_t const& fn );

/*! \brief Register highlighter callback.
*
* \param fn - user defined callback function.
*/
void set_highlighter_callback( highlighter_callback_t const& fn );

/*! \brief Register hints callback.
*
* \param fn - user defined callback function.
*/
void set_hint_callback( hint_callback_t const& fn );

/*! \brief Read line of user input.
*
* \param prompt - prompt to be displayed before getting user input.
* \return An UTF-8 encoded input given by the user (or nullptr on EOF).
*/
char const* input( std::string const& prompt );

/*! \brief Get current state data.
*
* This call is intended to be used in handlers.
*
* \return Current state of the model.
*/
State get_state( void ) const;

/*! \brief Set new state data.
*
* This call is intended to be used in handlers.
*
* \param state - new state of the model.
*/
void set_state( State const& state );

/*! \brief Print formatted string to standard output.
*
* This function ensures proper handling of ANSI escape sequences
* contained in printed data, which is especially useful on Windows
* since Unixes handle them correctly out of the box.
*
* \param fmt - printf style format.
*/
void print( char const* fmt, ... );

/*! \brief Schedule an emulated key press event.
*
* \param code - key press code to be emulated.
*/
void emulate_key_press( char32_t code );

/*! \brief Invoke built-in action handler.
*
* \pre This method can be called only from key-press handler.
*
* \param action - a built-in action to invoke.
* \param code - a supplementary key-code to consume by built-in action handler.
* \return The action result informing the replxx what shall happen next.
*/
ACTION_RESULT invoke( ACTION action, char32_t code );

/*! \brief Bind user defined action to handle given key-press event.
*
* \param code - handle this key-press event with following handler.
* \param handle - use this handler to handle key-press event.
*/
void bind_key( char32_t code, key_press_handler_t handler );

void history_add( std::string const& line );
int history_save( std::string const& filename );
int history_load( std::string const& filename );
int history_size( void ) const;
std::string history_line( int index );

void set_preload_buffer( std::string const& preloadText );

/*! \brief Set set of word break characters.
*
* This setting influences word based cursor movement and line editing capabilities.
*
* \param wordBreakers - 7-bit ASCII set of word breaking characters.
*/
void set_word_break_characters( char const* wordBreakers );

/*! \brief How many completions should trigger pagination.
*/
void set_completion_count_cutoff( int count );

/*! \brief Set maximum number of displayed hint rows.
*/
void set_max_hint_rows( int count );

/*! \brief Set a delay before hint are shown after user stopped typing..
*
* \param milliseconds - a number of milliseconds to wait before showing hints.
*/
void set_hint_delay( int milliseconds );

/*! \brief Set tab completion behavior.
*
* \param val - use double tab to invoke completions.
*/
void set_double_tab_completion( bool val );

/*! \brief Set tab completion behavior.
*
* \param val - invoke completion even if user input is empty.
*/
void set_complete_on_empty( bool val );

/*! \brief Set tab completion behavior.
*
* \param val - beep if completion is ambiguous.
*/
void set_beep_on_ambiguous_completion( bool val );

/*! \brief Disable output coloring.
*
* \param val - if set to non-zero disable output colors.
*/
void set_no_color( bool val );

/*! \brief Set maximum number of entries in history list.
*/
void set_max_history_size( int len );
void clear_screen( void );
int install_window_change_handler( void );

private:
Replxx( Replxx const& ) = delete;
Replxx& operator = ( Replxx const& ) = delete;
};

}

#endif /* HAVE_REPLXX_HXX_INCLUDED */


+ 271
- 0
contrib/replxx/src/ConvertUTF.cpp View File

@@ -0,0 +1,271 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/

/* ---------------------------------------------------------------------

Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
June 2002: Tim Dodd added detection and handling of incomplete
source sequences, enhanced error detection, added casts
to eliminate compiler warnings.
July 2003: slight mods to back out aggressive FFFE detection.
Jan 2004: updated switches in from-UTF8 conversions.
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.

See the header file "ConvertUTF.h" for complete documentation.

------------------------------------------------------------------------ */

#include "ConvertUTF.h"
#ifdef CVTUTF_DEBUG
#include <stdio.h>
#endif

namespace replxx {

#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_LOW_END (UTF32)0xDFFF

/* --------------------------------------------------------------------- */

/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
* left as-is for anyone who may want to do such conversion, which was
* allowed in earlier algorithms.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};

/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };

/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
* for *legal* UTF-8 will be 4 or fewer bytes total.
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };

/* --------------------------------------------------------------------- */

/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/

/* --------------------------------------------------------------------- */

/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/

static bool isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: { if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; } /* fall through */
case 3: { if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; } /* fall through */
case 2: {
if ((a = (*--srcptr)) > 0xBF) return false;

switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
} /* fall through */
case 1: { if (*source >= 0x80 && *source < 0xC2) return false; } /* fall through */
}
if (*source > 0xF4) return false;
return true;
}

/* --------------------------------------------------------------------- */

ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
ch = *source++;
if (flags == strictConversion ) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/*
* Figure out how many bytes the result will require. Turn any
* illegally large UTF32 things (> Plane 17) into replacement chars.
*/
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd) {
--source; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: { *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; } /* fall through */
case 3: { *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; } /* fall through */
case 2: { *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; } /* fall through */
case 1: { *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); } /* fall through */
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}

/* --------------------------------------------------------------------- */

ConversionResult ConvertUTF8toUTF32 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF32* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: { ch += *source++; ch <<= 6; } /* fall through */
case 4: { ch += *source++; ch <<= 6; } /* fall through */
case 3: { ch += *source++; ch <<= 6; } /* fall through */
case 2: { ch += *source++; ch <<= 6; } /* fall through */
case 1: { ch += *source++; ch <<= 6; } /* fall through */
case 0: { ch += *source++; } /* fall through */
}
ch -= offsetsFromUTF8[extraBytesToRead];

if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up the source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_LEGAL_UTF32) {
/*
* UTF-16 surrogate values are illegal in UTF-32, and anything
* over Plane 17 (> 0x10FFFF) is illegal.
*/
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = ch;
}
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
result = sourceIllegal;
*target++ = UNI_REPLACEMENT_CHAR;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}

}

/* ---------------------------------------------------------------------

Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.

--------------------------------------------------------------------- */

+ 139
- 0
contrib/replxx/src/ConvertUTF.h View File

@@ -0,0 +1,139 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/

/* ---------------------------------------------------------------------

Conversions between UTF32, UTF-16, and UTF-8. Header file.

Several funtions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.

Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.

Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.

The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)

After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.

Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.

These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.

Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.

When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.

Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.

Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.

------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */

#ifndef REPLXX_CONVERT_UTF8_H_INCLUDED
#define REPLXX_CONVERT_UTF8_H_INCLUDED 1

#if 0
typedef unsigned long UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
#endif

#include <stdint.h>
#include <string>

namespace replxx {

typedef uint32_t UTF32;
typedef uint16_t UTF16;
typedef uint8_t UTF8;

/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF

typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult;

typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;

ConversionResult ConvertUTF8toUTF32 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);

ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);

}

#endif /* REPLXX_CONVERT_UTF8_H_INCLUDED */

/* --------------------------------------------------------------------- */

+ 113
- 0
contrib/replxx/src/conversion.cxx View File

@@ -0,0 +1,113 @@
#include <algorithm>
#include <string>
#include <cstring>
#include <cctype>
#include <locale.h>

#include "conversion.hxx"

#ifdef _WIN32
#define strdup _strdup
#endif

using namespace std;

namespace replxx {

namespace locale {

void to_lower( std::string& s_ ) {
transform( s_.begin(), s_.end(), s_.begin(), static_cast<int(*)(int)>( &tolower ) );
}

bool is_8bit_encoding( void ) {
bool is8BitEncoding( false );
string origLC( setlocale( LC_CTYPE, nullptr ) );
string lc( origLC );
to_lower( lc );
if ( lc == "c" ) {
setlocale( LC_CTYPE, "" );
}
lc = setlocale( LC_CTYPE, nullptr );
setlocale( LC_CTYPE, origLC.c_str() );
to_lower( lc );
if ( lc.find( "8859" ) != std::string::npos ) {
is8BitEncoding = true;
}
return ( is8BitEncoding );
}

bool is8BitEncoding( is_8bit_encoding() );

}

ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, const char* src) {
ConversionResult res = ConversionResult::conversionOK;
if ( ! locale::is8BitEncoding ) {
const UTF8* sourceStart = reinterpret_cast<const UTF8*>(src);
const UTF8* sourceEnd = sourceStart + strlen(src);
UTF32* targetStart = reinterpret_cast<UTF32*>(dst);
UTF32* targetEnd = targetStart + dstSize;

res = ConvertUTF8toUTF32(
&sourceStart, sourceEnd, &targetStart, targetEnd, lenientConversion);

if (res == conversionOK) {
dstCount = targetStart - reinterpret_cast<UTF32*>(dst);

if (dstCount < dstSize) {
*targetStart = 0;
}
}
} else {
for ( dstCount = 0; ( dstCount < dstSize ) && src[dstCount]; ++ dstCount ) {
dst[dstCount] = src[dstCount];
}
}
return res;
}

ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, const char8_t* src) {
return copyString8to32(
dst, dstSize, dstCount, reinterpret_cast<const char*>(src)
);
}

void copyString32to8(
char* dst, int dstSize, const char32_t* src, int srcSize, int* dstCount
) {
if ( ! locale::is8BitEncoding ) {
const UTF32* sourceStart = reinterpret_cast<const UTF32*>(src);
const UTF32* sourceEnd = sourceStart + srcSize;
UTF8* targetStart = reinterpret_cast<UTF8*>(dst);
UTF8* targetEnd = targetStart + dstSize;

ConversionResult res = ConvertUTF32toUTF8(
&sourceStart, sourceEnd, &targetStart, targetEnd, lenientConversion);

if (res == conversionOK) {
int resCount( targetStart - reinterpret_cast<UTF8*>( dst ) );

if ( resCount < dstSize ) {
*targetStart = 0;
}
if ( dstCount ) {
*dstCount = resCount;
}
}
} else {
int i( 0 );
for ( i = 0; ( i < dstSize ) && ( i < srcSize ) && src[i]; ++ i ) {
dst[i] = static_cast<char>( src[i] );
}
if ( dstCount ) {
*dstCount = i;
}
if ( i < dstSize ) {
dst[i] = 0;
}
}
}

}


+ 20
- 0
contrib/replxx/src/conversion.hxx View File

@@ -0,0 +1,20 @@
#ifndef REPLXX_CONVERSION_HXX_INCLUDED
#define REPLXX_CONVERSION_HXX_INCLUDED 1

#include "ConvertUTF.h"

namespace replxx {

typedef unsigned char char8_t;

ConversionResult copyString8to32( char32_t* dst, int dstSize, int& dstCount, char const* src );
ConversionResult copyString8to32( char32_t* dst, int dstSize, int& dstCount, char8_t const* src );
void copyString32to8( char* dst, int dstSize, char32_t const* src, int srcSize, int* dstCount = nullptr );

namespace locale {
extern bool is8BitEncoding;
}

}

#endif

+ 860
- 0
contrib/replxx/src/escape.cxx View File

@@ -0,0 +1,860 @@
#include "escape.hxx"
#include "io.hxx"
#include "replxx.hxx"

#ifndef _WIN32

namespace replxx {

namespace EscapeSequenceProcessing { // move these out of global namespace

// This chunk of code does parsing of the escape sequences sent by various Linux
// terminals.
//
// It handles arrow keys, Home, End and Delete keys by interpreting the
// sequences sent by
// gnome terminal, xterm, rxvt, konsole, aterm and yakuake including the Alt and
// Ctrl key
// combinations that are understood by replxx.
//
// The parsing uses tables, a bunch of intermediate dispatch routines and a
// doDispatch
// loop that reads the tables and sends control to "deeper" routines to continue
// the
// parsing. The starting call to doDispatch( c, initialDispatch ) will
// eventually return
// either a character (with optional CTRL and META bits set), or -1 if parsing
// fails, or
// zero if an attempt to read from the keyboard fails.
//
// This is rather sloppy escape sequence processing, since we're not paying
// attention to what the
// actual TERM is set to and are processing all key sequences for all terminals,
// but it works with
// the most common keystrokes on the most common terminals. It's intricate, but
// the nested 'if'
// statements required to do it directly would be worse. This way has the
// advantage of allowing
// changes and extensions without having to touch a lot of code.


static char32_t thisKeyMetaCtrl = 0; // holds pre-set Meta and/or Ctrl modifiers

// This dispatch routine is given a dispatch table and then farms work out to
// routines
// listed in the table based on the character it is called with. The dispatch
// routines can
// read more input characters to decide what should eventually be returned.
// Eventually,
// a called routine returns either a character or -1 to indicate parsing
// failure.
//
char32_t doDispatch(char32_t c, CharacterDispatch& dispatchTable) {
for (unsigned int i = 0; i < dispatchTable.len; ++i) {
if (static_cast<unsigned char>(dispatchTable.chars[i]) == c) {
return dispatchTable.dispatch[i](c);
}
}
return dispatchTable.dispatch[dispatchTable.len](c);
}

// Final dispatch routines -- return something
//
static char32_t normalKeyRoutine(char32_t c) { return thisKeyMetaCtrl | c; }
static char32_t upArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::UP;;
}
static char32_t downArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::DOWN;
}
static char32_t rightArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::RIGHT;
}
static char32_t leftArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::LEFT;
}
static char32_t homeKeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::HOME; }
static char32_t endKeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::END; }
static char32_t shiftTabRoutine(char32_t) { return Replxx::KEY::BASE_SHIFT | Replxx::KEY::TAB; }
static char32_t f1KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F1; }
static char32_t f2KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F2; }
static char32_t f3KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F3; }
static char32_t f4KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F4; }
static char32_t f5KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F5; }
static char32_t f6KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F6; }
static char32_t f7KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F7; }
static char32_t f8KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F8; }
static char32_t f9KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F9; }
static char32_t f10KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F10; }
static char32_t f11KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F11; }
static char32_t f12KeyRoutine(char32_t) { return thisKeyMetaCtrl | Replxx::KEY::F12; }
static char32_t pageUpKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::PAGE_UP;
}
static char32_t pageDownKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::PAGE_DOWN;
}
static char32_t deleteCharRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::BACKSPACE;
} // key labeled Backspace
static char32_t insertKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::INSERT;
} // key labeled Delete
static char32_t deleteKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::DELETE;
} // key labeled Delete
static char32_t ctrlUpArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::BASE_CONTROL | Replxx::KEY::UP;
}
static char32_t ctrlDownArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::BASE_CONTROL | Replxx::KEY::DOWN;
}
static char32_t ctrlRightArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::BASE_CONTROL | Replxx::KEY::RIGHT;
}
static char32_t ctrlLeftArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::BASE_CONTROL | Replxx::KEY::LEFT;
}
static char32_t escFailureRoutine(char32_t) {
beep();
return -1;
}

// Handle ESC [ 1 ; 2 or 3 (or 5) <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket1Semicolon2or3or5Routines[] = {
upArrowKeyRoutine,
downArrowKeyRoutine,
rightArrowKeyRoutine,
leftArrowKeyRoutine,
homeKeyRoutine,
endKeyRoutine,
f1KeyRoutine,
f2KeyRoutine,
f3KeyRoutine,
f4KeyRoutine,
escFailureRoutine
};
static CharacterDispatch escLeftBracket1Semicolon2or3or5Dispatch = {
10, "ABCDHFPQRS", escLeftBracket1Semicolon2or3or5Routines
};

// Handle ESC [ 1 ; <more stuff> escape sequences
//
static char32_t escLeftBracket1Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket1Semicolon2or3or5Dispatch);
}
static char32_t escLeftBracket1Semicolon3Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_META;
return doDispatch(c, escLeftBracket1Semicolon2or3or5Dispatch);
}
static char32_t escLeftBracket1Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket1Semicolon2or3or5Dispatch);
}
static CharacterDispatchRoutine escLeftBracket1SemicolonRoutines[] = {
escLeftBracket1Semicolon2Routine,
escLeftBracket1Semicolon3Routine,
escLeftBracket1Semicolon5Routine,
escFailureRoutine
};
static CharacterDispatch escLeftBracket1SemicolonDispatch = {
3, "235", escLeftBracket1SemicolonRoutines
};

// Handle ESC [ 1 ; <more stuff> escape sequences
//
static char32_t escLeftBracket1SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket1SemicolonDispatch);
}

// (S)-F5
static CharacterDispatchRoutine escLeftBracket15Semicolon2Routines[] = {
f5KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket15Semicolon2Dispatch = {
1, "~", escLeftBracket15Semicolon2Routines
};
static char32_t escLeftBracket15Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket15Semicolon2Dispatch);
}

// (C)-F5
static CharacterDispatchRoutine escLeftBracket15Semicolon5Routines[] = {
f5KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket15Semicolon5Dispatch = {
1, "~", escLeftBracket15Semicolon5Routines
};
static char32_t escLeftBracket15Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket15Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket15SemicolonRoutines[] = {
escLeftBracket15Semicolon2Routine, escLeftBracket15Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket15SemicolonDispatch = {
2, "25", escLeftBracket15SemicolonRoutines
};
static char32_t escLeftBracket15SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket15SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket15Routines[] = {
f5KeyRoutine, escLeftBracket15SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket15Dispatch = {
2, "~;", escLeftBracket15Routines
};
static char32_t escLeftBracket15Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket15Dispatch);
}

// (S)-F6
static CharacterDispatchRoutine escLeftBracket17Semicolon2Routines[] = {
f6KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket17Semicolon2Dispatch = {
1, "~", escLeftBracket17Semicolon2Routines
};
static char32_t escLeftBracket17Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket17Semicolon2Dispatch);
}

// (C)-F6
static CharacterDispatchRoutine escLeftBracket17Semicolon5Routines[] = {
f6KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket17Semicolon5Dispatch = {
1, "~", escLeftBracket17Semicolon5Routines
};
static char32_t escLeftBracket17Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket17Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket17SemicolonRoutines[] = {
escLeftBracket17Semicolon2Routine, escLeftBracket17Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket17SemicolonDispatch = {
2, "25", escLeftBracket17SemicolonRoutines
};
static char32_t escLeftBracket17SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket17SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket17Routines[] = {
f6KeyRoutine, escLeftBracket17SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket17Dispatch = {
2, "~;", escLeftBracket17Routines
};
static char32_t escLeftBracket17Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket17Dispatch);
}

// (S)-F7
static CharacterDispatchRoutine escLeftBracket18Semicolon2Routines[] = {
f7KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket18Semicolon2Dispatch = {
1, "~", escLeftBracket18Semicolon2Routines
};
static char32_t escLeftBracket18Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket18Semicolon2Dispatch);
}

// (C)-F7
static CharacterDispatchRoutine escLeftBracket18Semicolon5Routines[] = {
f7KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket18Semicolon5Dispatch = {
1, "~", escLeftBracket18Semicolon5Routines
};
static char32_t escLeftBracket18Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket18Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket18SemicolonRoutines[] = {
escLeftBracket18Semicolon2Routine, escLeftBracket18Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket18SemicolonDispatch = {
2, "25", escLeftBracket18SemicolonRoutines
};
static char32_t escLeftBracket18SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket18SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket18Routines[] = {
f7KeyRoutine, escLeftBracket18SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket18Dispatch = {
2, "~;", escLeftBracket18Routines
};
static char32_t escLeftBracket18Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket18Dispatch);
}

// (S)-F8
static CharacterDispatchRoutine escLeftBracket19Semicolon2Routines[] = {
f8KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket19Semicolon2Dispatch = {
1, "~", escLeftBracket19Semicolon2Routines
};
static char32_t escLeftBracket19Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket19Semicolon2Dispatch);
}

// (C)-F8
static CharacterDispatchRoutine escLeftBracket19Semicolon5Routines[] = {
f8KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket19Semicolon5Dispatch = {
1, "~", escLeftBracket19Semicolon5Routines
};
static char32_t escLeftBracket19Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket19Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket19SemicolonRoutines[] = {
escLeftBracket19Semicolon2Routine, escLeftBracket19Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket19SemicolonDispatch = {
2, "25", escLeftBracket19SemicolonRoutines
};
static char32_t escLeftBracket19SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket19SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket19Routines[] = {
f8KeyRoutine, escLeftBracket19SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket19Dispatch = {
2, "~;", escLeftBracket19Routines
};
static char32_t escLeftBracket19Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket19Dispatch);
}

// Handle ESC [ 1 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket1Routines[] = {
homeKeyRoutine, escLeftBracket1SemicolonRoutine,
escLeftBracket15Routine,
escLeftBracket17Routine,
escLeftBracket18Routine,
escLeftBracket19Routine,
escFailureRoutine
};
static CharacterDispatch escLeftBracket1Dispatch = {
6, "~;5789", escLeftBracket1Routines
};

// Handle ESC [ 2 <more stuff> escape sequences
//

// (S)-F9
static CharacterDispatchRoutine escLeftBracket20Semicolon2Routines[] = {
f9KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket20Semicolon2Dispatch = {
1, "~", escLeftBracket20Semicolon2Routines
};
static char32_t escLeftBracket20Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket20Semicolon2Dispatch);
}

// (C)-F9
static CharacterDispatchRoutine escLeftBracket20Semicolon5Routines[] = {
f9KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket20Semicolon5Dispatch = {
1, "~", escLeftBracket20Semicolon5Routines
};
static char32_t escLeftBracket20Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket20Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket20SemicolonRoutines[] = {
escLeftBracket20Semicolon2Routine, escLeftBracket20Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket20SemicolonDispatch = {
2, "25", escLeftBracket20SemicolonRoutines
};
static char32_t escLeftBracket20SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket20SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket20Routines[] = {
f9KeyRoutine, escLeftBracket20SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket20Dispatch = {
2, "~;", escLeftBracket20Routines
};
static char32_t escLeftBracket20Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket20Dispatch);
}

// (S)-F10
static CharacterDispatchRoutine escLeftBracket21Semicolon2Routines[] = {
f10KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket21Semicolon2Dispatch = {
1, "~", escLeftBracket21Semicolon2Routines
};
static char32_t escLeftBracket21Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket21Semicolon2Dispatch);
}

// (C)-F10
static CharacterDispatchRoutine escLeftBracket21Semicolon5Routines[] = {
f10KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket21Semicolon5Dispatch = {
1, "~", escLeftBracket21Semicolon5Routines
};
static char32_t escLeftBracket21Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket21Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket21SemicolonRoutines[] = {
escLeftBracket21Semicolon2Routine, escLeftBracket21Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket21SemicolonDispatch = {
2, "25", escLeftBracket21SemicolonRoutines
};
static char32_t escLeftBracket21SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket21SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket21Routines[] = {
f10KeyRoutine, escLeftBracket21SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket21Dispatch = {
2, "~;", escLeftBracket21Routines
};
static char32_t escLeftBracket21Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket21Dispatch);
}

// (S)-F11
static CharacterDispatchRoutine escLeftBracket23Semicolon2Routines[] = {
f11KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket23Semicolon2Dispatch = {
1, "~", escLeftBracket23Semicolon2Routines
};
static char32_t escLeftBracket23Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket23Semicolon2Dispatch);
}

// (C)-F11
static CharacterDispatchRoutine escLeftBracket23Semicolon5Routines[] = {
f11KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket23Semicolon5Dispatch = {
1, "~", escLeftBracket23Semicolon5Routines
};
static char32_t escLeftBracket23Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket23Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket23SemicolonRoutines[] = {
escLeftBracket23Semicolon2Routine, escLeftBracket23Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket23SemicolonDispatch = {
2, "25", escLeftBracket23SemicolonRoutines
};
static char32_t escLeftBracket23SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket23SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket23Routines[] = {
f11KeyRoutine, escLeftBracket23SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket23Dispatch = {
2, "~;", escLeftBracket23Routines
};
static char32_t escLeftBracket23Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket23Dispatch);
}

// (S)-F12
static CharacterDispatchRoutine escLeftBracket24Semicolon2Routines[] = {
f12KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket24Semicolon2Dispatch = {
1, "~", escLeftBracket24Semicolon2Routines
};
static char32_t escLeftBracket24Semicolon2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_SHIFT;
return doDispatch(c, escLeftBracket24Semicolon2Dispatch);
}

// (C)-F12
static CharacterDispatchRoutine escLeftBracket24Semicolon5Routines[] = {
f12KeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket24Semicolon5Dispatch = {
1, "~", escLeftBracket24Semicolon5Routines
};
static char32_t escLeftBracket24Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket24Semicolon5Dispatch);
}

static CharacterDispatchRoutine escLeftBracket24SemicolonRoutines[] = {
escLeftBracket24Semicolon2Routine, escLeftBracket24Semicolon5Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket24SemicolonDispatch = {
2, "25", escLeftBracket24SemicolonRoutines
};
static char32_t escLeftBracket24SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket24SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket24Routines[] = {
f12KeyRoutine, escLeftBracket24SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket24Dispatch = {
2, "~;", escLeftBracket24Routines
};
static char32_t escLeftBracket24Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket24Dispatch);
}

// Handle ESC [ 2 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket2Routines[] = {
insertKeyRoutine,
escLeftBracket20Routine,
escLeftBracket21Routine,
escLeftBracket23Routine,
escLeftBracket24Routine,
escFailureRoutine
};
static CharacterDispatch escLeftBracket2Dispatch = {
5, "~0134", escLeftBracket2Routines
};

// Handle ESC [ 3 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket3Routines[] = {
deleteKeyRoutine, escFailureRoutine
};

static CharacterDispatch escLeftBracket3Dispatch = {
1, "~", escLeftBracket3Routines
};

// Handle ESC [ 4 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket4Routines[] = {
endKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket4Dispatch = {
1, "~", escLeftBracket4Routines
};

// Handle ESC [ 5 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket5Semicolon5Routines[] = {
pageUpKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket5Semicolon5Dispatch = {
1, "~", escLeftBracket5Semicolon5Routines
};
static char32_t escLeftBracket5Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket5Semicolon5Dispatch);
}
static CharacterDispatchRoutine escLeftBracket5SemicolonRoutines[] = {
escLeftBracket5Semicolon5Routine,
escFailureRoutine
};
static CharacterDispatch escLeftBracket5SemicolonDispatch = {
1, "5", escLeftBracket5SemicolonRoutines
};
static char32_t escLeftBracket5SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket5SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket5Routines[] = {
pageUpKeyRoutine, escLeftBracket5SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket5Dispatch = {
2, "~;", escLeftBracket5Routines
};

// Handle ESC [ 6 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket6Semicolon5Routines[] = {
pageDownKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket6Semicolon5Dispatch = {
1, "~", escLeftBracket6Semicolon5Routines
};
static char32_t escLeftBracket6Semicolon5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
thisKeyMetaCtrl |= Replxx::KEY::BASE_CONTROL;
return doDispatch(c, escLeftBracket6Semicolon5Dispatch);
}
static CharacterDispatchRoutine escLeftBracket6SemicolonRoutines[] = {
escLeftBracket6Semicolon5Routine,
escFailureRoutine
};
static CharacterDispatch escLeftBracket6SemicolonDispatch = {
1, "5", escLeftBracket6SemicolonRoutines
};
static char32_t escLeftBracket6SemicolonRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket6SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket6Routines[] = {
pageDownKeyRoutine, escLeftBracket6SemicolonRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket6Dispatch = {
2, "~;", escLeftBracket6Routines
};

// Handle ESC [ 7 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket7Routines[] = {
homeKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket7Dispatch = {
1, "~", escLeftBracket7Routines
};

// Handle ESC [ 8 <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracket8Routines[] = {
endKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket8Dispatch = {
1, "~", escLeftBracket8Routines
};

// Handle ESC [ <digit> escape sequences
//
static char32_t escLeftBracket0Routine(char32_t c) {
return escFailureRoutine(c);
}
static char32_t escLeftBracket1Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket1Dispatch);
}
static char32_t escLeftBracket2Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket2Dispatch);
}
static char32_t escLeftBracket3Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket3Dispatch);
}
static char32_t escLeftBracket4Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket4Dispatch);
}
static char32_t escLeftBracket5Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket5Dispatch);
}
static char32_t escLeftBracket6Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket6Dispatch);
}
static char32_t escLeftBracket7Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket7Dispatch);
}
static char32_t escLeftBracket8Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket8Dispatch);
}
static char32_t escLeftBracket9Routine(char32_t c) {
return escFailureRoutine(c);
}

// Handle ESC [ <more stuff> escape sequences
//
static CharacterDispatchRoutine escLeftBracketRoutines[] = {
upArrowKeyRoutine, downArrowKeyRoutine, rightArrowKeyRoutine,
leftArrowKeyRoutine, homeKeyRoutine, endKeyRoutine,
shiftTabRoutine,
escLeftBracket0Routine, escLeftBracket1Routine, escLeftBracket2Routine,
escLeftBracket3Routine, escLeftBracket4Routine, escLeftBracket5Routine,
escLeftBracket6Routine, escLeftBracket7Routine, escLeftBracket8Routine,
escLeftBracket9Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracketDispatch = {17, "ABCDHFZ0123456789",
escLeftBracketRoutines};

// Handle ESC O <char> escape sequences
//
static CharacterDispatchRoutine escORoutines[] = {
upArrowKeyRoutine, downArrowKeyRoutine, rightArrowKeyRoutine,
leftArrowKeyRoutine, homeKeyRoutine, endKeyRoutine,
f1KeyRoutine, f2KeyRoutine, f3KeyRoutine,
f4KeyRoutine,
ctrlUpArrowKeyRoutine, ctrlDownArrowKeyRoutine, ctrlRightArrowKeyRoutine,
ctrlLeftArrowKeyRoutine, escFailureRoutine
};
static CharacterDispatch escODispatch = {14, "ABCDHFPQRSabcd", escORoutines};

// Initial ESC dispatch -- could be a Meta prefix or the start of an escape
// sequence
//
static char32_t escLeftBracketRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracketDispatch);
}
static char32_t escORoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escODispatch);
}
static char32_t setMetaRoutine(char32_t c); // need forward reference
static CharacterDispatchRoutine escRoutines[] = {
escLeftBracketRoutine, escORoutine, setMetaRoutine
};
static CharacterDispatch escDispatch = {2, "[O", escRoutines};

// Initial dispatch -- we are not in the middle of anything yet
//
static char32_t escRoutine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escDispatch);
}
static CharacterDispatchRoutine initialRoutines[] = {
escRoutine, deleteCharRoutine, normalKeyRoutine
};
static CharacterDispatch initialDispatch = {2, "\x1B\x7F", initialRoutines};

// Special handling for the ESC key because it does double duty
//
static char32_t setMetaRoutine(char32_t c) {
thisKeyMetaCtrl = Replxx::KEY::BASE_META;
if (c == 0x1B) { // another ESC, stay in ESC processing mode
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escDispatch);
}
return doDispatch(c, initialDispatch);
}

char32_t doDispatch(char32_t c) {
EscapeSequenceProcessing::thisKeyMetaCtrl = 0; // no modifiers yet at initialDispatch
return doDispatch(c, initialDispatch);
}

} // namespace EscapeSequenceProcessing // move these out of global namespace

}

#endif /* #ifndef _WIN32 */


+ 37
- 0
contrib/replxx/src/escape.hxx View File

@@ -0,0 +1,37 @@
#ifndef REPLXX_ESCAPE_HXX_INCLUDED
#define REPLXX_ESCAPE_HXX_INCLUDED 1

namespace replxx {

namespace EscapeSequenceProcessing {

// This is a typedef for the routine called by doDispatch(). It takes the
// current character
// as input, does any required processing including reading more characters and
// calling other
// dispatch routines, then eventually returns the final (possibly extended or
// special) character.
//
typedef char32_t (*CharacterDispatchRoutine)(char32_t);

// This structure is used by doDispatch() to hold a list of characters to test
// for and
// a list of routines to call if the character matches. The dispatch routine
// list is one
// longer than the character list; the final entry is used if no character
// matches.
//
struct CharacterDispatch {
unsigned int len; // length of the chars list
const char* chars; // chars to test
CharacterDispatchRoutine* dispatch; // array of routines to call
};

char32_t doDispatch(char32_t c);

}

}

#endif


+ 148
- 0
contrib/replxx/src/history.cxx View File

@@ -0,0 +1,148 @@
#include <fstream>
#include <cstring>

#ifndef _WIN32

#include <unistd.h>
#include <sys/stat.h>

#endif /* _WIN32 */

#include "history.hxx"
#include "utf8string.hxx"

using namespace std;

namespace replxx {

static int const REPLXX_DEFAULT_HISTORY_MAX_LEN( 1000 );

History::History( void )
: _data()
, _maxSize( REPLXX_DEFAULT_HISTORY_MAX_LEN )
, _maxLineLength( 0 )
, _index( 0 )
, _previousIndex( -2 )
, _recallMostRecent( false ) {
}

void History::add( UnicodeString const& line ) {
if ( ( _maxSize > 0 ) && ( _data.empty() || ( line != _data.back() ) ) ) {
if ( size() > _maxSize ) {
_data.erase( _data.begin() );
if ( -- _previousIndex < -1 ) {
_previousIndex = -2;
}
}
if ( static_cast<int>( line.length() ) > _maxLineLength ) {
_maxLineLength = static_cast<int>( line.length() );
}
_data.push_back( line );
}
}

int History::save( std::string const& filename ) {
#ifndef _WIN32
mode_t old_umask = umask( S_IXUSR | S_IRWXG| S_IRWXO );
#endif
ofstream histFile( filename );
if ( ! histFile ) {
return ( -1 );
}
#ifndef _WIN32
umask( old_umask );
chmod( filename.c_str(), S_IRUSR | S_IWUSR );
#endif
Utf8String utf8;
for ( UnicodeString const& h : _data ) {
if ( ! h.is_empty() ) {
utf8.assign( h );
histFile << utf8.get() << endl;
}
}
return ( 0 );
}

int History::load( std::string const& filename ) {
ifstream histFile( filename );
if ( ! histFile ) {
return ( -1 );
}
string line;
while ( getline( histFile, line ).good() ) {
string::size_type eol( line.find_first_of( "\r\n" ) );
if ( eol != string::npos ) {
line.erase( eol );
}
if ( ! line.empty() ) {
add( UnicodeString( line ) );
}
}
return 0;
}

void History::set_max_size( int size_ ) {
if ( size_ >= 0 ) {
_maxSize = size_;
int curSize( size() );
if ( _maxSize < curSize ) {
_data.erase( _data.begin(), _data.begin() + ( curSize - _maxSize ) );
}
}
}

void History::reset_pos( int pos_ ) {
if ( pos_ == -1 ) {
_index = size() - 1;
_recallMostRecent = false;
} else {
_index = pos_;
}
}

bool History::move( bool up_ ) {
if (_previousIndex != -2 && ! up_ ) {
_index = 1 + _previousIndex; // emulate Windows down-arrow
} else {
_index += up_ ? -1 : 1;
}
_previousIndex = -2;
if (_index < 0) {
_index = 0;
return ( false );
} else if ( _index >= size() ) {
_index = size() - 1;
return ( false );
}
_recallMostRecent = true;
return ( true );
}

void History::jump( bool start_ ) {
_index = start_ ? 0 : size() - 1;
_previousIndex = -2;
_recallMostRecent = true;
}

bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize_, bool back_ ) {
int direct( size() + ( back_ ? -1 : 1 ) );
int i( ( _index + direct ) % _data.size() );
while ( i != _index ) {
if ( _data[i].starts_with( prefix_.begin(), prefix_.begin() + prefixSize_ ) ) {
_index = i;
_previousIndex = -2;
_recallMostRecent = true;
return ( true );
}
i += direct;
i %= _data.size();
}
return ( false );
}

UnicodeString const& History::operator[] ( int idx_ ) const {
return ( _data[ idx_ ] );
}

}


+ 73
- 0
contrib/replxx/src/history.hxx View File

@@ -0,0 +1,73 @@
#ifndef REPLXX_HISTORY_HXX_INCLUDED
#define REPLXX_HISTORY_HXX_INCLUDED 1

#include <vector>

#include "unicodestring.hxx"
#include "conversion.hxx"

namespace replxx {

class History {
public:
typedef std::vector<UnicodeString> lines_t;
private:
lines_t _data;
int _maxSize;
int _maxLineLength;
int _index;
int _previousIndex;
bool _recallMostRecent;
public:
History( void );
void add( UnicodeString const& line );
int save( std::string const& filename );
int load( std::string const& filename );
void set_max_size( int len );
void reset_pos( int = -1 );
UnicodeString const& operator[] ( int ) const;
void set_recall_most_recent( void ) {
_recallMostRecent = true;
}
void reset_recall_most_recent( void ) {
_recallMostRecent = false;
}
void drop_last( void ) {
_data.pop_back();
}
void commit_index( void ) {
_previousIndex = _recallMostRecent ? _index : -2;
}
int current_pos( void ) const {
return ( _index );
}
bool is_last( void ) const {
return ( _index == ( size() - 1 ) );
}
bool is_empty( void ) const {
return ( _data.empty() );
}
void update_last( UnicodeString const& line_ ) {
_data.back() = line_;
}
bool move( bool );
UnicodeString const& current( void ) const {
return ( _data[_index] );
}
void jump( bool );
bool common_prefix_search( UnicodeString const&, int, bool );
int size( void ) const {
return ( static_cast<int>( _data.size() ) );
}
int max_line_length( void ) {
return ( _maxLineLength );
}
private:
History( History const& ) = delete;
History& operator = ( History const& ) = delete;
};

}

#endif


+ 674
- 0
contrib/replxx/src/io.cxx View File

@@ -0,0 +1,674 @@
#include <memory>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <array>

#ifdef _WIN32

#include <conio.h>
#include <windows.h>
#include <io.h>
#define isatty _isatty
#define strcasecmp _stricmp
#define strdup _strdup
#define write _write
#define STDIN_FILENO 0

#include "windows.hxx"

#else /* _WIN32 */

#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <fcntl.h>

#endif /* _WIN32 */

#include "io.hxx"
#include "conversion.hxx"
#include "escape.hxx"
#include "replxx.hxx"
#include "util.hxx"

using namespace std;

namespace replxx {

namespace tty {

bool is_a_tty( int fd_ ) {
bool aTTY( isatty( fd_ ) != 0 );
#ifdef _WIN32
do {
if ( aTTY ) {
break;
}
HANDLE h( (HANDLE)_get_osfhandle( fd_ ) );
if ( h == INVALID_HANDLE_VALUE ) {
break;
}
DWORD st( 0 );
if ( ! GetConsoleMode( h, &st ) ) {
break;
}
aTTY = true;
} while ( false );
#endif
return ( aTTY );
}

bool in( is_a_tty( 0 ) );
bool out( is_a_tty( 1 ) );

}

Terminal::Terminal( void )
#ifdef _WIN32
: _consoleOut( INVALID_HANDLE_VALUE )
, _consoleIn( INVALID_HANDLE_VALUE )
, _oldMode()
, _oldDisplayAttribute()
, _inputCodePage( GetConsoleCP() )
, _outputCodePage( GetConsoleOutputCP() )
, _interrupt( INVALID_HANDLE_VALUE )
, _events()
#else
: _origTermios()
, _interrupt()
#endif
, _rawMode( false ) {
#ifdef _WIN32
_interrupt = CreateEvent( nullptr, true, false, TEXT( "replxx_interrupt_event" ) );
#else
static_cast<void>( ::pipe( _interrupt ) == 0 );
#endif
}

Terminal::~Terminal( void ) {
if ( _rawMode ) {
disable_raw_mode();
}
#ifdef _WIN32
CloseHandle( _interrupt );
#else
static_cast<void>( ::close( _interrupt[0] ) == 0 );
static_cast<void>( ::close( _interrupt[1] ) == 0 );
#endif
}

void Terminal::write32( char32_t const* text32, int len32 ) {
int len8 = 4 * len32 + 1;
unique_ptr<char[]> text8(new char[len8]);
int count8 = 0;

copyString32to8(text8.get(), len8, text32, len32, &count8);
int nWritten( 0 );
#ifdef _WIN32
nWritten = win_write( text8.get(), count8 );
#else
nWritten = write( 1, text8.get(), count8 );
#endif
if ( nWritten != count8 ) {
throw std::runtime_error( "write failed" );
}
return;
}

void Terminal::write8( char const* data_, int size_ ) {
#ifdef _WIN32
int nWritten( win_write( data_, size_ ) );
#else
int nWritten( write( 1, data_, size_ ) );
#endif
if ( nWritten != size_ ) {
throw std::runtime_error( "write failed" );
}
return;
}

int Terminal::get_screen_columns( void ) {
int cols( 0 );
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO inf;
GetConsoleScreenBufferInfo( _consoleOut, &inf );
cols = inf.dwSize.X;
#else
struct winsize ws;
cols = ( ioctl( 1, TIOCGWINSZ, &ws ) == -1 ) ? 80 : ws.ws_col;
#endif
// cols is 0 in certain circumstances like inside debugger, which creates
// further issues
return ( cols > 0 ) ? cols : 80;
}

int Terminal::get_screen_rows( void ) {
int rows;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO inf;
GetConsoleScreenBufferInfo( _consoleOut, &inf );
rows = 1 + inf.srWindow.Bottom - inf.srWindow.Top;
#else
struct winsize ws;
rows = (ioctl(1, TIOCGWINSZ, &ws) == -1) ? 24 : ws.ws_row;
#endif
return (rows > 0) ? rows : 24;
}

namespace {
inline int notty( void ) {
errno = ENOTTY;
return ( -1 );
}
}

int Terminal::enable_raw_mode( void ) {
if ( ! _rawMode ) {
#ifdef _WIN32
_consoleIn = GetStdHandle( STD_INPUT_HANDLE );
_consoleOut = GetStdHandle( STD_OUTPUT_HANDLE );
SetConsoleCP( 65001 );
SetConsoleOutputCP( 65001 );
GetConsoleMode( _consoleIn, &_oldMode );
SetConsoleMode(
_consoleIn,
_oldMode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT )
);
#else
struct termios raw;

if ( ! tty::in ) {
return ( notty() );
}
if ( tcgetattr( 0, &_origTermios ) == -1 ) {
return ( notty() );
}

raw = _origTermios; /* modify the original mode */
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* output modes - disable post processing */
// this is wrong, we don't want raw output, it turns newlines into straight
// linefeeds
// raw.c_oflag &= ~(OPOST);
/* control modes - set 8 bit chars */
raw.c_cflag |= (CS8);
/* local modes - echoing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte, without timeout. */
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0; /* 1 byte, no timer */

/* put terminal in raw mode after flushing */
if ( tcsetattr(0, TCSADRAIN, &raw) < 0 ) {
return ( notty() );
}
#endif
_rawMode = true;
}
return 0;
}

void Terminal::disable_raw_mode(void) {
if ( _rawMode ) {
#ifdef _WIN32
SetConsoleMode( _consoleIn, _oldMode );
SetConsoleCP( _inputCodePage );
SetConsoleOutputCP( _outputCodePage );
_consoleIn = INVALID_HANDLE_VALUE;
_consoleOut = INVALID_HANDLE_VALUE;
#else
if ( tcsetattr( 0, TCSADRAIN, &_origTermios ) == -1 ) {
return;
}
#endif
_rawMode = false;
}
}

#ifndef _WIN32

/**
* Read a UTF-8 sequence from the non-Windows keyboard and return the Unicode
* (char32_t) character it encodes
*
* @return char32_t Unicode character
*/
char32_t read_unicode_character(void) {
static char8_t utf8String[5];
static size_t utf8Count = 0;
while (true) {
char8_t c;

/* Continue reading if interrupted by signal. */
ssize_t nread;
do {
nread = read( STDIN_FILENO, &c, 1 );
} while ((nread == -1) && (errno == EINTR));

if (nread <= 0) return 0;
if (c <= 0x7F || locale::is8BitEncoding) { // short circuit ASCII
utf8Count = 0;
return c;
} else if (utf8Count < sizeof(utf8String) - 1) {
utf8String[utf8Count++] = c;
utf8String[utf8Count] = 0;
char32_t unicodeChar[2];
int ucharCount( 0 );
ConversionResult res = copyString8to32(unicodeChar, 2, ucharCount, utf8String);
if (res == conversionOK && ucharCount) {
utf8Count = 0;
return unicodeChar[0];
}
} else {
utf8Count = 0; // this shouldn't happen: got four bytes but no UTF-8 character
}
}
}

#endif // #ifndef _WIN32

void beep() {
fprintf(stderr, "\x7"); // ctrl-G == bell/beep
fflush(stderr);
}

// replxx_read_char -- read a keystroke or keychord from the keyboard, and translate it
// into an encoded "keystroke". When convenient, extended keys are translated into their
// simpler Emacs keystrokes, so an unmodified "left arrow" becomes Ctrl-B.
//
// A return value of zero means "no input available", and a return value of -1
// means "invalid key".
//
char32_t Terminal::read_char( void ) {
char32_t c( 0 );
#ifdef _WIN32
INPUT_RECORD rec;
DWORD count;
char32_t modifierKeys = 0;
bool escSeen = false;
int highSurrogate( 0 );
while (true) {
ReadConsoleInputW( _consoleIn, &rec, 1, &count );
#if __REPLXX_DEBUG__ // helper for debugging keystrokes, display info in the debug "Output"
// window in the debugger
{
if ( rec.EventType == KEY_EVENT ) {
//if ( rec.Event.KeyEvent.uChar.UnicodeChar ) {
char buf[1024];
sprintf(
buf,
"Unicode character 0x%04X, repeat count %d, virtual keycode 0x%04X, "
"virtual scancode 0x%04X, key %s%s%s%s%s\n",
rec.Event.KeyEvent.uChar.UnicodeChar,
rec.Event.KeyEvent.wRepeatCount,
rec.Event.KeyEvent.wVirtualKeyCode,
rec.Event.KeyEvent.wVirtualScanCode,
rec.Event.KeyEvent.bKeyDown ? "down" : "up",
(rec.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED) ? " L-Ctrl" : "",
(rec.Event.KeyEvent.dwControlKeyState & RIGHT_CTRL_PRESSED) ? " R-Ctrl" : "",
(rec.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED) ? " L-Alt" : "",
(rec.Event.KeyEvent.dwControlKeyState & RIGHT_ALT_PRESSED) ? " R-Alt" : ""
);
OutputDebugStringA( buf );
//}
}
}
#endif
if (rec.EventType != KEY_EVENT) {
continue;
}
// Windows provides for entry of characters that are not on your keyboard by
// sending the
// Unicode characters as a "key up" with virtual keycode 0x12 (VK_MENU ==
// Alt key) ...
// accept these characters, otherwise only process characters on "key down"
if (!rec.Event.KeyEvent.bKeyDown &&
rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU) {
continue;
}
modifierKeys = 0;
// AltGr is encoded as ( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED ), so don't
// treat this
// combination as either CTRL or META we just turn off those two bits, so it
// is still
// possible to combine CTRL and/or META with an AltGr key by using
// right-Ctrl and/or
// left-Alt
if ((rec.Event.KeyEvent.dwControlKeyState &
(LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) ==
(LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) {
rec.Event.KeyEvent.dwControlKeyState &=
~(LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
}
if ( rec.Event.KeyEvent.dwControlKeyState & ( RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED ) ) {
modifierKeys |= Replxx::KEY::BASE_CONTROL;
}
if ( rec.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) ) {
modifierKeys |= Replxx::KEY::BASE_META;
}
if (escSeen) {
modifierKeys |= Replxx::KEY::BASE_META;
}
int key( rec.Event.KeyEvent.uChar.UnicodeChar );
if ( key == 0 ) {
switch (rec.Event.KeyEvent.wVirtualKeyCode) {
case VK_LEFT:
return modifierKeys | Replxx::KEY::LEFT;
case VK_RIGHT:
return modifierKeys | Replxx::KEY::RIGHT;
case VK_UP:
return modifierKeys | Replxx::KEY::UP;
case VK_DOWN:
return modifierKeys | Replxx::KEY::DOWN;
case VK_DELETE:
return modifierKeys | Replxx::KEY::DELETE;
case VK_HOME:
return modifierKeys | Replxx::KEY::HOME;
case VK_END:
return modifierKeys | Replxx::KEY::END;
case VK_PRIOR:
return modifierKeys | Replxx::KEY::PAGE_UP;
case VK_NEXT:
return modifierKeys | Replxx::KEY::PAGE_DOWN;
case VK_F1:
return modifierKeys | Replxx::KEY::F1;
case VK_F2:
return modifierKeys | Replxx::KEY::F2;
case VK_F3:
return modifierKeys | Replxx::KEY::F3;
case VK_F4:
return modifierKeys | Replxx::KEY::F4;
case VK_F5:
return modifierKeys | Replxx::KEY::F5;
case VK_F6:
return modifierKeys | Replxx::KEY::F6;
case VK_F7:
return modifierKeys | Replxx::KEY::F7;
case VK_F8:
return modifierKeys | Replxx::KEY::F8;
case VK_F9:
return modifierKeys | Replxx::KEY::F9;
case VK_F10:
return modifierKeys | Replxx::KEY::F10;
case VK_F11:
return modifierKeys | Replxx::KEY::F11;
case VK_F12:
return modifierKeys | Replxx::KEY::F12;
default:
continue; // in raw mode, ReadConsoleInput shows shift, ctrl - ignore them
}
} else if ( key == Replxx::KEY::ESCAPE ) { // ESC, set flag for later
escSeen = true;
continue;
} else if ( ( key >= 0xD800 ) && ( key <= 0xDBFF ) ) {
highSurrogate = key - 0xD800;
continue;
} else {
// we got a real character, return it
if ( ( key >= 0xDC00 ) && ( key <= 0xDFFF ) ) {
key -= 0xDC00;
key |= ( highSurrogate << 10 );
key += 0x10000;
}
if ( is_control_code( key ) ) {
key += 0x40;
modifierKeys |= Replxx::KEY::BASE_CONTROL;
}
key |= modifierKeys;
highSurrogate = 0;
c = key;
break;
}
}

#else
c = read_unicode_character();
if (c == 0) {
return 0;
}

// If _DEBUG_LINUX_KEYBOARD is set, then ctrl-^ puts us into a keyboard
// debugging mode
// where we print out decimal and decoded values for whatever the "terminal"
// program
// gives us on different keystrokes. Hit ctrl-C to exit this mode.
//
#ifdef __REPLXX_DEBUG__
if (c == ctrlChar('^')) { // ctrl-^, special debug mode, prints all keys hit,
// ctrl-C to get out
printf(
"\nEntering keyboard debugging mode (on ctrl-^), press ctrl-C to exit "
"this mode\n");
while (true) {
unsigned char keys[10];
int ret = read(0, keys, 10);

if (ret <= 0) {
printf("\nret: %d\n", ret);
}
for (int i = 0; i < ret; ++i) {
char32_t key = static_cast<char32_t>(keys[i]);
char* friendlyTextPtr;
char friendlyTextBuf[10];
const char* prefixText = (key < 0x80) ? "" : "0x80+";
char32_t keyCopy = (key < 0x80) ? key : key - 0x80;
if (keyCopy >= '!' && keyCopy <= '~') { // printable
friendlyTextBuf[0] = '\'';
friendlyTextBuf[1] = keyCopy;
friendlyTextBuf[2] = '\'';
friendlyTextBuf[3] = 0;
friendlyTextPtr = friendlyTextBuf;
} else if (keyCopy == ' ') {
friendlyTextPtr = const_cast<char*>("space");
} else if (keyCopy == 27) {
friendlyTextPtr = const_cast<char*>("ESC");
} else if (keyCopy == 0) {
friendlyTextPtr = const_cast<char*>("NUL");
} else if (keyCopy == 127) {
friendlyTextPtr = const_cast<char*>("DEL");
} else {
friendlyTextBuf[0] = '^';
friendlyTextBuf[1] = keyCopy + 0x40;
friendlyTextBuf[2] = 0;
friendlyTextPtr = friendlyTextBuf;
}
printf("%d x%02X (%s%s) ", key, key, prefixText, friendlyTextPtr);
}
printf("\x1b[1G\n"); // go to first column of new line

// drop out of this loop on ctrl-C
if (keys[0] == ctrlChar('C')) {
printf("Leaving keyboard debugging mode (on ctrl-C)\n");
fflush(stdout);
return -2;
}
}
}
#endif // __REPLXX_DEBUG__

c = EscapeSequenceProcessing::doDispatch(c);
if ( is_control_code( c ) ) {
c = Replxx::KEY::control( c + 0x40 );
}
#endif // #_WIN32
return ( c );
}

Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) {
#ifdef _WIN32
std::array<HANDLE,2> handles = { _consoleIn, _interrupt };
while ( true ) {
DWORD event( WaitForMultipleObjects( handles.size (), handles.data(), false, timeout_ > 0 ? timeout_ : INFINITE ) );
switch ( event ) {
case ( WAIT_OBJECT_0 + 0 ): {
// peek events that will be skipped
INPUT_RECORD rec;
DWORD count;
PeekConsoleInputW( _consoleIn, &rec, 1, &count );

if (
( rec.EventType != KEY_EVENT )
|| ( !rec.Event.KeyEvent.bKeyDown && ( rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU ) )
) {
// read the event to unsignal the handle
ReadConsoleInputW( _consoleIn, &rec, 1, &count );
continue;
} else if (rec.EventType == KEY_EVENT) {
int key(rec.Event.KeyEvent.uChar.UnicodeChar);
if (key == 0) {
switch (rec.Event.KeyEvent.wVirtualKeyCode) {
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_DELETE:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
break;
default:
ReadConsoleInputW(_consoleIn, &rec, 1, &count);
continue; // in raw mode, ReadConsoleInput shows shift, ctrl - ignore them
}
}
}

return ( EVENT_TYPE::KEY_PRESS );
}
case ( WAIT_OBJECT_0 + 1 ): {
ResetEvent( _interrupt );
if ( _events.empty() ) {
continue;
}
EVENT_TYPE eventType( _events.front() );
_events.pop_front();
return ( eventType );
}
case ( WAIT_TIMEOUT ): {
return ( EVENT_TYPE::TIMEOUT );
}
}
}
#else
fd_set fdSet;
int nfds( max( _interrupt[0], _interrupt[1] ) + 1 );
while ( true ) {
FD_ZERO( &fdSet );
FD_SET( 0, &fdSet );
FD_SET( _interrupt[0], &fdSet );
timeval tv{ timeout_ / 1000, static_cast<suseconds_t>( ( timeout_ % 1000 ) * 1000 ) };
int err( select( nfds, &fdSet, nullptr, nullptr, timeout_ > 0 ? &tv : nullptr ) );
if ( ( err == -1 ) && ( errno == EINTR ) ) {
continue;
}
if ( err == 0 ) {
return ( EVENT_TYPE::TIMEOUT );
}
if ( FD_ISSET( _interrupt[0], &fdSet ) ) {
char data( 0 );
static_cast<void>( read( _interrupt[0], &data, 1 ) == 1 );
if ( data == 'k' ) {
return ( EVENT_TYPE::KEY_PRESS );
}
if ( data == 'm' ) {
return ( EVENT_TYPE::MESSAGE );
}
}
if ( FD_ISSET( 0, &fdSet ) ) {
return ( EVENT_TYPE::KEY_PRESS );
}
}
#endif
}

void Terminal::notify_event( EVENT_TYPE eventType_ ) {
#ifdef _WIN32
_events.push_back( eventType_ );
SetEvent( _interrupt );
#else
char data( eventType_ == EVENT_TYPE::KEY_PRESS ? 'k' : 'm' );
static_cast<void>( write( _interrupt[1], &data, 1 ) == 1 );
#endif
}

/**
* Clear the screen ONLY (no redisplay of anything)
*/
void Terminal::clear_screen( CLEAR_SCREEN clearScreen_ ) {
#ifdef _WIN32
COORD coord = { 0, 0 };
CONSOLE_SCREEN_BUFFER_INFO inf;
bool toEnd( clearScreen_ == CLEAR_SCREEN::TO_END );
HANDLE consoleOut( _consoleOut != INVALID_HANDLE_VALUE ? _consoleOut : GetStdHandle( STD_OUTPUT_HANDLE ) );
GetConsoleScreenBufferInfo( consoleOut, &inf );
if ( ! toEnd ) {
SetConsoleCursorPosition( consoleOut, coord );
} else {
coord = inf.dwCursorPosition;
}
DWORD nWritten( 0 );
DWORD toWrite(
toEnd
? ( inf.dwSize.Y - inf.dwCursorPosition.Y ) * inf.dwSize.X - inf.dwCursorPosition.X
: inf.dwSize.X * inf.dwSize.Y
);
FillConsoleOutputCharacterA( consoleOut, ' ', toWrite, coord, &nWritten );
#else
if ( clearScreen_ == CLEAR_SCREEN::WHOLE ) {
char const clearCode[] = "\033c\033[H\033[2J\033[0m";
static_cast<void>( write(1, clearCode, sizeof ( clearCode ) - 1) >= 0 );
} else {
char const clearCode[] = "\033[J";
static_cast<void>( write(1, clearCode, sizeof ( clearCode ) - 1) >= 0 );
}
#endif
}

void Terminal::jump_cursor( int xPos_, int yOffset_ ) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO inf;
GetConsoleScreenBufferInfo( _consoleOut, &inf );
inf.dwCursorPosition.X = xPos_;
inf.dwCursorPosition.Y += yOffset_;
SetConsoleCursorPosition( _consoleOut, inf.dwCursorPosition );
#else
char seq[64];
if ( yOffset_ != 0 ) { // move the cursor up as required
snprintf( seq, sizeof seq, "\033[%d%c", abs( yOffset_ ), yOffset_ > 0 ? 'B' : 'A' );
write8( seq, strlen( seq ) );
}
// position at the end of the prompt, clear to end of screen
snprintf(
seq, sizeof seq, "\033[%dG",
xPos_ + 1 /* 1-based on VT100 */
);
write8( seq, strlen( seq ) );
#endif
}

#ifndef _WIN32
int Terminal::read_verbatim( char32_t* buffer_, int size_ ) {
int len( 0 );
buffer_[len ++] = read_unicode_character();
int statusFlags( ::fcntl( STDIN_FILENO, F_GETFL, 0 ) );
::fcntl( STDIN_FILENO, F_SETFL, statusFlags | O_NONBLOCK );
while ( len < size_ ) {
char32_t c( read_unicode_character() );
if ( c == 0 ) {
break;
}
buffer_[len ++] = c;
}
::fcntl( STDIN_FILENO, F_SETFL, statusFlags );
return ( len );
}
#endif

}


+ 79
- 0
contrib/replxx/src/io.hxx View File

@@ -0,0 +1,79 @@
#ifndef REPLXX_IO_HXX_INCLUDED
#define REPLXX_IO_HXX_INCLUDED 1

#include <deque>

#ifdef _WIN32
#include <windows.h>
#else
#include <termios.h>
#endif

namespace replxx {

class Terminal {
public:
enum class EVENT_TYPE {
KEY_PRESS,
MESSAGE,
TIMEOUT
};
private:
#ifdef _WIN32
HANDLE _consoleOut;
HANDLE _consoleIn;
DWORD _oldMode;
WORD _oldDisplayAttribute;
UINT const _inputCodePage;
UINT const _outputCodePage;
HANDLE _interrupt;
typedef std::deque<EVENT_TYPE> events_t;
events_t _events;
#else
struct termios _origTermios; /* in order to restore at exit */
int _interrupt[2];
#endif
bool _rawMode; /* for destructor to check if restore is needed */
public:
enum class CLEAR_SCREEN {
WHOLE,
TO_END
};
public:
Terminal( void );
~Terminal( void );
void write32( char32_t const*, int );
void write8( char const*, int );
int get_screen_columns(void);
int get_screen_rows(void);
int enable_raw_mode(void);
void disable_raw_mode(void);
char32_t read_char(void);
void clear_screen( CLEAR_SCREEN );
EVENT_TYPE wait_for_input( int long = 0 );
void notify_event( EVENT_TYPE );
void jump_cursor( int, int );
#ifndef _WIN32
int read_verbatim( char32_t*, int );
#endif
private:
Terminal( Terminal const& ) = delete;
Terminal& operator = ( Terminal const& ) = delete;
Terminal( Terminal&& ) = delete;
Terminal& operator = ( Terminal&& ) = delete;
};

void beep();
char32_t read_unicode_character(void);

namespace tty {

extern bool in;
extern bool out;

}

}

#endif


+ 76
- 0
contrib/replxx/src/killring.hxx View File

@@ -0,0 +1,76 @@
#ifndef REPLXX_KILLRING_HXX_INCLUDED
#define REPLXX_KILLRING_HXX_INCLUDED 1

#include <vector>

#include "unicodestring.hxx"

namespace replxx {

class KillRing {
static const int capacity = 10;
int size;
int index;
char indexToSlot[10];
std::vector<UnicodeString> theRing;

public:
enum action { actionOther, actionKill, actionYank };
action lastAction;
size_t lastYankSize;

KillRing() : size(0), index(0), lastAction(actionOther) {
theRing.reserve(capacity);
}

void kill(const char32_t* text, int textLen, bool forward) {
if (textLen == 0) {
return;
}
UnicodeString killedText(text, textLen);
if (lastAction == actionKill && size > 0) {
int slot = indexToSlot[0];
int currentLen = static_cast<int>(theRing[slot].length());
UnicodeString temp;
if ( forward ) {
temp.append( theRing[slot].get(), currentLen ).append( killedText.get(), textLen );
} else {
temp.append( killedText.get(), textLen ).append( theRing[slot].get(), currentLen );
}
theRing[slot] = temp;
} else {
if (size < capacity) {
if (size > 0) {
memmove(&indexToSlot[1], &indexToSlot[0], size);
}
indexToSlot[0] = size;
size++;
theRing.push_back(killedText);
} else {
int slot = indexToSlot[capacity - 1];
theRing[slot] = killedText;
memmove(&indexToSlot[1], &indexToSlot[0], capacity - 1);
indexToSlot[0] = slot;
}
index = 0;
}
}

UnicodeString* yank() { return (size > 0) ? &theRing[indexToSlot[index]] : 0; }

UnicodeString* yankPop() {
if (size == 0) {
return 0;
}
++index;
if (index == size) {
index = 0;
}
return &theRing[indexToSlot[index]];
}
};

}

#endif


+ 150
- 0
contrib/replxx/src/prompt.cxx View File

@@ -0,0 +1,150 @@
#ifdef _WIN32

#include <conio.h>
#include <windows.h>
#include <io.h>
#if _MSC_VER < 1900 && defined (_MSC_VER)
#define snprintf _snprintf // Microsoft headers use underscores in some names
#endif
#define strcasecmp _stricmp
#define strdup _strdup
#define write _write
#define STDIN_FILENO 0

#else /* _WIN32 */

#include <unistd.h>

#endif /* _WIN32 */

#include "prompt.hxx"
#include "util.hxx"

namespace replxx {

Prompt::Prompt( Terminal& terminal_ )
: _extraLines( 0 )
, _lastLinePosition( 0 )
, _previousInputLen( 0 )
, _previousLen( 0 )
, _screenColumns( 0 )
, _terminal( terminal_ ) {
}

void Prompt::write() {
_terminal.write32( _text.get(), _byteCount );
}

void Prompt::update_screen_columns( void ) {
_screenColumns = _terminal.get_screen_columns();
}

void Prompt::set_text( UnicodeString const& text_ ) {
update_screen_columns();
// strip control characters from the prompt -- we do allow newline
_text = text_;
UnicodeString::const_iterator in( text_.begin() );
UnicodeString::iterator out( _text.begin() );

int len = 0;
int x = 0;

bool const strip = !tty::out;

while (in != text_.end()) {
char32_t c = *in;
if ('\n' == c || !is_control_code(c)) {
*out = c;
++out;
++in;
++len;
if ('\n' == c || ++x >= _screenColumns) {
x = 0;
++_extraLines;
_lastLinePosition = len;
}
} else if (c == '\x1b') {
if ( strip ) {
// jump over control chars
++in;
if (*in == '[') {
++in;
while ( ( in != text_.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
++in;
}
if (*in == 'm') {
++in;
}
}
} else {
// copy control chars
*out = *in;
++out;
++in;
if (*in == '[') {
*out = *in;
++out;
++in;
while ( ( in != text_.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
*out = *in;
++out;
++in;
}
if (*in == 'm') {
*out = *in;
++out;
++in;
}
}
}
} else {
++in;
}
}
_characterCount = len;
_byteCount = static_cast<int>(out - _text.begin());

_indentation = len - _lastLinePosition;
_cursorRowOffset = _extraLines;
}

// Used with DynamicPrompt (history search)
//
const UnicodeString forwardSearchBasePrompt("(i-search)`");
const UnicodeString reverseSearchBasePrompt("(reverse-i-search)`");
const UnicodeString endSearchBasePrompt("': ");
UnicodeString previousSearchText; // remembered across invocations of replxx_input()

DynamicPrompt::DynamicPrompt( Terminal& terminal_, int initialDirection )
: Prompt( terminal_ )
, _searchText()
, _direction( initialDirection ) {
update_screen_columns();
_cursorRowOffset = 0;
const UnicodeString* basePrompt =
(_direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
size_t promptStartLength = basePrompt->length();
_characterCount = static_cast<int>(promptStartLength + endSearchBasePrompt.length());
_byteCount = _characterCount;
_lastLinePosition = _characterCount; // TODO fix this, we are asssuming
// that the history prompt won't wrap (!)
_previousLen = _characterCount;
_text.assign( *basePrompt ).append( endSearchBasePrompt );
calculate_screen_position(
0, 0, screen_columns(), _characterCount,
_indentation, _extraLines
);
}

void DynamicPrompt::updateSearchPrompt(void) {
const UnicodeString* basePrompt =
(_direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
size_t promptStartLength = basePrompt->length();
_characterCount = static_cast<int>(promptStartLength + _searchText.length() +
endSearchBasePrompt.length());
_byteCount = _characterCount;
_text.assign( *basePrompt ).append( _searchText ).append( endSearchBasePrompt );
}

}


+ 49
- 0
contrib/replxx/src/prompt.hxx View File

@@ -0,0 +1,49 @@
#ifndef REPLXX_PROMPT_HXX_INCLUDED
#define REPLXX_PROMPT_HXX_INCLUDED 1

#include <cstdlib>

#include "unicodestring.hxx"
#include "io.hxx"

namespace replxx {

class Prompt { // a convenience struct for grouping prompt info
public:
UnicodeString _text; // our copy of the prompt text, edited
int _characterCount; // chars in _text
int _byteCount; // bytes in _text
int _extraLines; // extra lines (beyond 1) occupied by prompt
int _indentation; // column offset to end of prompt
int _lastLinePosition; // index into _text where last line begins
int _previousInputLen; // _characterCount of previous input line, for clearing
int _cursorRowOffset; // where the cursor is relative to the start of the prompt
int _previousLen; // help erasing
private:
int _screenColumns; // width of screen in columns [cache]
Terminal& _terminal;
public:
Prompt( Terminal& );
void set_text( UnicodeString const& textPtr );
void update_screen_columns( void );
int screen_columns() const {
return ( _screenColumns );
}
void write();
};

extern UnicodeString previousSearchText; // remembered across invocations of replxx_input()

// changing prompt for "(reverse-i-search)`text':" etc.
//
struct DynamicPrompt : public Prompt {
UnicodeString _searchText; // text we are searching for
int _direction; // current search _direction, 1=forward, -1=reverse

DynamicPrompt( Terminal&, int initialDirection );
void updateSearchPrompt(void);
};

}

#endif

+ 521
- 0
contrib/replxx/src/replxx.cxx View File

@@ -0,0 +1,521 @@
/*
* Copyright (c) 2017-2018, Marcin Konarski (amok at codestation.org)
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* line editing lib needs to be 20,000 lines of C code.
*
* You can find the latest source code at:
*
* http://github.com/antirez/linenoise
*
* Does a number of crazy assumptions that happen to be true in 99.9999% of
* the 2010 UNIX computers around.
*
* References:
* - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
*
* Todo list:
* - Switch to gets() if $TERM is something we can't support.
* - Filter bogus Ctrl+<char> combinations.
* - Win32 support
*
* Bloat:
* - Completion?
* - History search like Ctrl+r in readline?
*
* List of escape sequences used by this program, we do everything just
* with three sequences. In order to be so cheap we may have some
* flickering effect with some slow terminal, but the lesser sequences
* the more compatible.
*
* CHA (Cursor Horizontal Absolute)
* Sequence: ESC [ n G
* Effect: moves cursor to column n (1 based)
*
* EL (Erase Line)
* Sequence: ESC [ n K
* Effect: if n is 0 or missing, clear from cursor to end of line
* Effect: if n is 1, clear from beginning of line to cursor
* Effect: if n is 2, clear entire line
*
* CUF (Cursor Forward)
* Sequence: ESC [ n C
* Effect: moves cursor forward of n chars
*
* The following are used to clear the screen: ESC [ H ESC [ 2 J
* This is actually composed of two sequences:
*
* cursorhome
* Sequence: ESC [ H
* Effect: moves the cursor to upper left corner
*
* ED2 (Clear entire screen)
* Sequence: ESC [ 2 J
* Effect: clear the whole screen
*
*/

#include <algorithm>
#include <cstdarg>

#ifdef _WIN32

#include <io.h>
#define STDIN_FILENO 0

#else /* _WIN32 */

#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>

#endif /* _WIN32 */

#include "replxx.h"
#include "replxx.hxx"
#include "replxx_impl.hxx"
#include "io.hxx"

using namespace std;
using namespace std::placeholders;
using namespace replxx;

namespace replxx {

namespace {
void delete_ReplxxImpl( Replxx::ReplxxImpl* impl_ ) {
delete impl_;
}
}

Replxx::Replxx( void )
: _impl( new Replxx::ReplxxImpl( nullptr, nullptr, nullptr ), delete_ReplxxImpl ) {
}

void Replxx::set_completion_callback( completion_callback_t const& fn ) {
_impl->set_completion_callback( fn );
}

void Replxx::set_highlighter_callback( highlighter_callback_t const& fn ) {
_impl->set_highlighter_callback( fn );
}

void Replxx::set_hint_callback( hint_callback_t const& fn ) {
_impl->set_hint_callback( fn );
}

char const* Replxx::input( std::string const& prompt ) {
return ( _impl->input( prompt ) );
}

void Replxx::history_add( std::string const& line ) {
_impl->history_add( line );
}

int Replxx::history_save( std::string const& filename ) {
return ( _impl->history_save( filename ) );
}

int Replxx::history_load( std::string const& filename ) {
return ( _impl->history_load( filename ) );
}

int Replxx::history_size( void ) const {
return ( _impl->history_size() );
}

std::string Replxx::history_line( int index ) {
return ( _impl->history_line( index ) );
}

void Replxx::set_preload_buffer( std::string const& preloadText ) {
_impl->set_preload_buffer( preloadText );
}

void Replxx::set_word_break_characters( char const* wordBreakers ) {
_impl->set_word_break_characters( wordBreakers );
}

void Replxx::set_max_hint_rows( int count ) {
_impl->set_max_hint_rows( count );
}

void Replxx::set_hint_delay( int milliseconds ) {
_impl->set_hint_delay( milliseconds );
}

void Replxx::set_completion_count_cutoff( int count ) {
_impl->set_completion_count_cutoff( count );
}

void Replxx::set_double_tab_completion( bool val ) {
_impl->set_double_tab_completion( val );
}

void Replxx::set_complete_on_empty( bool val ) {
_impl->set_complete_on_empty( val );
}

void Replxx::set_beep_on_ambiguous_completion( bool val ) {
_impl->set_beep_on_ambiguous_completion( val );
}

void Replxx::set_no_color( bool val ) {
_impl->set_no_color( val );
}

void Replxx::set_max_history_size( int len ) {
_impl->set_max_history_size( len );
}

void Replxx::clear_screen( void ) {
_impl->clear_screen( 0 );
}

void Replxx::emulate_key_press( char32_t keyPress_ ) {
_impl->emulate_key_press( keyPress_ );
}

Replxx::ACTION_RESULT Replxx::invoke( ACTION action_, char32_t keyPress_ ) {
return ( _impl->invoke( action_, keyPress_ ) );
}

void Replxx::bind_key( char32_t keyPress_, key_press_handler_t handler_ ) {
_impl->bind_key( keyPress_, handler_ );
}

Replxx::State Replxx::get_state( void ) const {
return ( _impl->get_state() );
}

void Replxx::set_state( Replxx::State const& state_ ) {
_impl->set_state( state_ );
}

int Replxx::install_window_change_handler( void ) {
return ( _impl->install_window_change_handler() );
}

void Replxx::print( char const* format_, ... ) {
::std::va_list ap;
va_start( ap, format_ );
int size = static_cast<int>( vsnprintf( nullptr, 0, format_, ap ) );
va_end( ap );
va_start( ap, format_ );
unique_ptr<char[]> buf( new char[size + 1] );
vsnprintf( buf.get(), static_cast<size_t>( size + 1 ), format_, ap );
va_end( ap );
return ( _impl->print( buf.get(), size ) );
}

}

::Replxx* replxx_init() {
typedef ::Replxx* replxx_data_t;
return ( reinterpret_cast<replxx_data_t>( new replxx::Replxx::ReplxxImpl( nullptr, nullptr, nullptr ) ) );
}

void replxx_end( ::Replxx* replxx_ ) {
delete reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ );
}

void replxx_clear_screen( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->clear_screen( 0 );
}

void replxx_emulate_key_press( ::Replxx* replxx_, int unsigned keyPress_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->emulate_key_press( keyPress_ );
}

ReplxxActionResult replxx_invoke( ::Replxx* replxx_, ReplxxAction action_, int unsigned keyPress_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( static_cast<ReplxxActionResult>( replxx->invoke( static_cast<replxx::Replxx::ACTION>( action_ ), keyPress_ ) ) );
}

replxx::Replxx::ACTION_RESULT key_press_handler_forwarder( key_press_handler_t handler_, char32_t code_, void* userData_ ) {
return ( static_cast<replxx::Replxx::ACTION_RESULT>( handler_( code_, userData_ ) ) );
}

void replxx_bind_key( ::Replxx* replxx_, int code_, key_press_handler_t handler_, void* userData_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->bind_key( code_, std::bind( key_press_handler_forwarder, handler_, _1, userData_ ) );
}

void replxx_get_state( ::Replxx* replxx_, ReplxxState* state ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx::Replxx::State s( replxx->get_state() );
state->text = s.text();
state->cursorPosition = s.cursor_position();
}

void replxx_set_state( ::Replxx* replxx_, ReplxxState* state ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_state( replxx::Replxx::State( state->text, state->cursorPosition ) );
}

/**
* replxx_set_preload_buffer provides text to be inserted into the command buffer
*
* the provided text will be processed to be usable and will be used to preload
* the input buffer on the next call to replxx_input()
*
* @param preloadText text to begin with on the next call to replxx_input()
*/
void replxx_set_preload_buffer(::Replxx* replxx_, const char* preloadText) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_preload_buffer( preloadText ? preloadText : "" );
}

/**
* replxx_input is a readline replacement.
*
* call it with a prompt to display and it will return a line of input from the
* user
*
* @param prompt text of prompt to display to the user
* @return the returned string belongs to the caller on return and must be
* freed to prevent memory leaks
*/
char const* replxx_input( ::Replxx* replxx_, const char* prompt ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->input( prompt ) );
}

int replxx_print( ::Replxx* replxx_, char const* format_, ... ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
::std::va_list ap;
va_start( ap, format_ );
int size = static_cast<int>( vsnprintf( nullptr, 0, format_, ap ) );
va_end( ap );
va_start( ap, format_ );
unique_ptr<char[]> buf( new char[size + 1] );
vsnprintf( buf.get(), static_cast<size_t>( size + 1 ), format_, ap );
va_end( ap );
try {
replxx->print( buf.get(), size );
} catch ( ... ) {
return ( -1 );
}
return ( size );
}

struct replxx_completions {
replxx::Replxx::completions_t data;
};

struct replxx_hints {
replxx::Replxx::hints_t data;
};

replxx::Replxx::completions_t completions_fwd( replxx_completion_callback_t fn, std::string const& input_, int& contextLen_, void* userData ) {
replxx_completions completions;
fn( input_.c_str(), &completions, &contextLen_, userData );
return ( completions.data );
}

/* Register a callback function to be called for tab-completion. */
void replxx_set_completion_callback(::Replxx* replxx_, replxx_completion_callback_t* fn, void* userData) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_completion_callback( std::bind( &completions_fwd, fn, _1, _2, userData ) );
}

void highlighter_fwd( replxx_highlighter_callback_t fn, std::string const& input, replxx::Replxx::colors_t& colors, void* userData ) {
std::vector<ReplxxColor> colorsTmp( colors.size() );
std::transform(
colors.begin(),
colors.end(),
colorsTmp.begin(),
[]( replxx::Replxx::Color c ) {
return ( static_cast<ReplxxColor>( c ) );
}
);
fn( input.c_str(), colorsTmp.data(), colors.size(), userData );
std::transform(
colorsTmp.begin(),
colorsTmp.end(),
colors.begin(),
[]( ReplxxColor c ) {
return ( static_cast<replxx::Replxx::Color>( c ) );
}
);
}

void replxx_set_highlighter_callback( ::Replxx* replxx_, replxx_highlighter_callback_t* fn, void* userData ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_highlighter_callback( std::bind( &highlighter_fwd, fn, _1, _2, userData ) );
}

replxx::Replxx::hints_t hints_fwd( replxx_hint_callback_t fn, std::string const& input_, int& contextLen_, replxx::Replxx::Color& color_, void* userData ) {
replxx_hints hints;
ReplxxColor c( static_cast<ReplxxColor>( color_ ) );
fn( input_.c_str(), &hints, &contextLen_, &c, userData );
return ( hints.data );
}

void replxx_set_hint_callback( ::Replxx* replxx_, replxx_hint_callback_t* fn, void* userData ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_hint_callback( std::bind( &hints_fwd, fn, _1, _2, _3, userData ) );
}

void replxx_add_hint(replxx_hints* lh, const char* str) {
lh->data.emplace_back(str);
}

void replxx_add_completion( replxx_completions* lc, const char* str ) {
lc->data.emplace_back( str );
}

void replxx_add_completion( replxx_completions* lc, const char* str, ReplxxColor color ) {
lc->data.emplace_back( str, static_cast<replxx::Replxx::Color>( color ) );
}

void replxx_history_add( ::Replxx* replxx_, const char* line ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->history_add( line );
}

void replxx_set_max_history_size( ::Replxx* replxx_, int len ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_max_history_size( len );
}

void replxx_set_max_hint_rows( ::Replxx* replxx_, int count ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_max_hint_rows( count );
}

void replxx_set_hint_delay( ::Replxx* replxx_, int milliseconds ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_hint_delay( milliseconds );
}

void replxx_set_completion_count_cutoff( ::Replxx* replxx_, int count ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_completion_count_cutoff( count );
}

void replxx_set_word_break_characters( ::Replxx* replxx_, char const* breakChars_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_word_break_characters( breakChars_ );
}

void replxx_set_double_tab_completion( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_double_tab_completion( val ? true : false );
}

void replxx_set_complete_on_empty( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_complete_on_empty( val ? true : false );
}

void replxx_set_no_color( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_no_color( val ? true : false );
}

void replxx_set_beep_on_ambiguous_completion( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_beep_on_ambiguous_completion( val ? true : false );
}

/* Fetch a line of the history by (zero-based) index. If the requested
* line does not exist, NULL is returned. The return value is a heap-allocated
* copy of the line. */
char const* replxx_history_line( ::Replxx* replxx_, int index ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_line( index ).c_str() );
}

/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int replxx_history_save( ::Replxx* replxx_, const char* filename ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_save( filename ) );
}

/* Load the history from the specified file. If the file does not exist
* zero is returned and no operation is performed.
*
* If the file exists and the operation succeeded 0 is returned, otherwise
* on error -1 is returned. */
int replxx_history_load( ::Replxx* replxx_, const char* filename ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_load( filename ) );
}

int replxx_history_size( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_size() );
}

/* This special mode is used by replxx in order to print scan codes
* on screen for debugging / development purposes. It is implemented
* by the replxx-c-api-example program using the --keycodes option. */
#ifdef __REPLXX_DEBUG__
void replxx_debug_dump_print_codes(void) {
char quit[4];

printf(
"replxx key codes debugging mode.\n"
"Press keys to see scan codes. Type 'quit' at any time to exit.\n");
if (enableRawMode() == -1) return;
memset(quit, ' ', 4);
while (1) {
char c;
int nread;

#if _WIN32
nread = _read(STDIN_FILENO, &c, 1);
#else
nread = read(STDIN_FILENO, &c, 1);
#endif
if (nread <= 0) continue;
memmove(quit, quit + 1, sizeof(quit) - 1); /* shift string to left. */
quit[sizeof(quit) - 1] = c; /* Insert current char on the right. */
if (memcmp(quit, "quit", sizeof(quit)) == 0) break;

printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c,
(int)c);
printf("\r"); /* Go left edge manually, we are in raw mode. */
fflush(stdout);
}
disableRawMode();
}
#endif // __REPLXX_DEBUG__

int replxx_install_window_change_handler( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->install_window_change_handler() );
}


+ 1984
- 0
contrib/replxx/src/replxx_impl.cxx
File diff suppressed because it is too large
View File


+ 243
- 0
contrib/replxx/src/replxx_impl.hxx View File

@@ -0,0 +1,243 @@
/*
* Copyright (c) 2017-2018, Marcin Konarski (amok at codestation.org)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef HAVE_REPLXX_REPLXX_IMPL_HXX_INCLUDED
#define HAVE_REPLXX_REPLXX_IMPL_HXX_INCLUDED 1

#include <vector>
#include <deque>
#include <memory>
#include <string>
#include <unordered_map>
#include <thread>
#include <mutex>

#include "replxx.hxx"
#include "history.hxx"
#include "killring.hxx"
#include "utf8string.hxx"
#include "prompt.hxx"
#include "io.hxx"

namespace replxx {

class Replxx::ReplxxImpl {
public:
class Completion {
UnicodeString _text;
Replxx::Color _color;
public:
Completion( UnicodeString const& text_, Replxx::Color color_ )
: _text( text_ )
, _color( color_ ) {
}
Completion( Replxx::Completion const& completion_ )
: _text( completion_.text() )
, _color( completion_.color() ) {
}
Completion( Completion const& ) = default;
Completion& operator = ( Completion const& ) = default;
Completion( Completion&& ) = default;
Completion& operator = ( Completion&& ) = default;
UnicodeString const& text( void ) const {
return ( _text );
}
Replxx::Color color( void ) const {
return ( _color );
}
};
typedef std::vector<Completion> completions_t;
typedef std::vector<UnicodeString> hints_t;
typedef std::unique_ptr<char[]> utf8_buffer_t;
typedef std::unique_ptr<char32_t[]> input_buffer_t;
typedef std::vector<char> char_widths_t;
typedef std::vector<char32_t> display_t;
typedef std::deque<char32_t> key_presses_t;
typedef std::deque<std::string> messages_t;
enum class HINT_ACTION {
REGENERATE,
REPAINT,
TRIM,
SKIP
};
typedef Replxx::ACTION_RESULT ( ReplxxImpl::* key_press_handler_raw_t )( char32_t );
typedef std::unordered_map<int, Replxx::key_press_handler_t> key_press_handlers_t;
private:
typedef int long long unsigned action_trait_t;
static action_trait_t const NOOP = 0;
static action_trait_t const WANT_REFRESH = 1;
static action_trait_t const RESET_KILL_ACTION = 2;
static action_trait_t const SET_KILL_ACTION = 4;
static action_trait_t const DONT_RESET_PREFIX = 8;
static action_trait_t const DONT_RESET_COMPLETIONS = 16;
private:
mutable Utf8String _utf8Buffer;
UnicodeString _data;
char_widths_t _charWidths; // character widths from mk_wcwidth()
display_t _display;
int _displayInputLength;
UnicodeString _hint;
int _pos; // character position in buffer ( 0 <= _pos <= _len )
int _prefix; // prefix length used in common prefix search
int _hintSelection; // Currently selected hint.
History _history;
KillRing _killRing;
int _maxHintRows;
int _hintDelay;
char const* _breakChars;
int _completionCountCutoff;
bool _overwrite;
bool _doubleTabCompletion;
bool _completeOnEmpty;
bool _beepOnAmbiguousCompletion;
bool _noColor;
key_press_handlers_t _keyPressHandlers;
Terminal _terminal;
std::thread::id _currentThread;
Prompt _prompt;
Replxx::completion_callback_t _completionCallback;
Replxx::highlighter_callback_t _highlighterCallback;
Replxx::hint_callback_t _hintCallback;
key_presses_t _keyPresses;
messages_t _messages;
completions_t _completions;
int _completionContextLength;
int _completionSelection;
std::string _preloadedBuffer; // used with set_preload_buffer
std::string _errorMessage;
bool _modifiedState;
mutable std::mutex _mutex;
public:
ReplxxImpl( FILE*, FILE*, FILE* );
void set_completion_callback( Replxx::completion_callback_t const& fn );
void set_highlighter_callback( Replxx::highlighter_callback_t const& fn );
void set_hint_callback( Replxx::hint_callback_t const& fn );
char const* input( std::string const& prompt );
void history_add( std::string const& line );
int history_save( std::string const& filename );
int history_load( std::string const& filename );
std::string history_line( int index );
int history_size( void ) const;
void set_preload_buffer(std::string const& preloadText);
void set_word_break_characters( char const* wordBreakers );
void set_max_hint_rows( int count );
void set_hint_delay( int milliseconds );
void set_double_tab_completion( bool val );
void set_complete_on_empty( bool val );
void set_beep_on_ambiguous_completion( bool val );
void set_no_color( bool val );
void set_max_history_size( int len );
void set_completion_count_cutoff( int len );
int install_window_change_handler( void );
completions_t call_completer( std::string const& input, int& ) const;
hints_t call_hinter( std::string const& input, int&, Replxx::Color& color ) const;
void print( char const*, int );
Replxx::ACTION_RESULT clear_screen( char32_t );
void emulate_key_press( char32_t );
Replxx::ACTION_RESULT invoke( Replxx::ACTION, char32_t );
void bind_key( char32_t, Replxx::key_press_handler_t );
Replxx::State get_state( void ) const;
void set_state( Replxx::State const& );
private:
ReplxxImpl( ReplxxImpl const& ) = delete;
ReplxxImpl& operator = ( ReplxxImpl const& ) = delete;
private:
void preload_puffer( char const* preloadText );
int get_input_line( void );
Replxx::ACTION_RESULT action( action_trait_t, key_press_handler_raw_t const&, char32_t );
Replxx::ACTION_RESULT insert_character( char32_t );
Replxx::ACTION_RESULT go_to_begining_of_line( char32_t );
Replxx::ACTION_RESULT go_to_end_of_line( char32_t );
Replxx::ACTION_RESULT move_one_char_left( char32_t );
Replxx::ACTION_RESULT move_one_char_right( char32_t );
Replxx::ACTION_RESULT move_one_word_left( char32_t );
Replxx::ACTION_RESULT move_one_word_right( char32_t );
Replxx::ACTION_RESULT kill_word_to_left( char32_t );
Replxx::ACTION_RESULT kill_word_to_right( char32_t );
Replxx::ACTION_RESULT kill_to_whitespace_to_left( char32_t );
Replxx::ACTION_RESULT kill_to_begining_of_line( char32_t );
Replxx::ACTION_RESULT kill_to_end_of_line( char32_t );
Replxx::ACTION_RESULT yank( char32_t );
Replxx::ACTION_RESULT yank_cycle( char32_t );
Replxx::ACTION_RESULT capitalize_word( char32_t );
Replxx::ACTION_RESULT lowercase_word( char32_t );
Replxx::ACTION_RESULT uppercase_word( char32_t );
Replxx::ACTION_RESULT transpose_characters( char32_t );
Replxx::ACTION_RESULT abort_line( char32_t );
Replxx::ACTION_RESULT send_eof( char32_t );
Replxx::ACTION_RESULT delete_character( char32_t );
Replxx::ACTION_RESULT backspace_character( char32_t );
Replxx::ACTION_RESULT commit_line( char32_t );
Replxx::ACTION_RESULT history_next( char32_t );
Replxx::ACTION_RESULT history_previous( char32_t );
Replxx::ACTION_RESULT history_move( bool );
Replxx::ACTION_RESULT history_first( char32_t );
Replxx::ACTION_RESULT history_last( char32_t );
Replxx::ACTION_RESULT history_jump( bool );
Replxx::ACTION_RESULT hint_next( char32_t );
Replxx::ACTION_RESULT hint_previous( char32_t );
Replxx::ACTION_RESULT hint_move( bool );
Replxx::ACTION_RESULT toggle_overwrite_mode( char32_t );
#ifndef _WIN32
Replxx::ACTION_RESULT verbatim_insert( char32_t );
Replxx::ACTION_RESULT suspend( char32_t );
#endif
Replxx::ACTION_RESULT complete_line( char32_t );
Replxx::ACTION_RESULT complete_next( char32_t );
Replxx::ACTION_RESULT complete_previous( char32_t );
Replxx::ACTION_RESULT complete( bool );
Replxx::ACTION_RESULT incremental_history_search( char32_t startChar );
Replxx::ACTION_RESULT common_prefix_search( char32_t startChar );
char32_t read_char( HINT_ACTION = HINT_ACTION::SKIP );
char const* read_from_stdin( void );
char32_t do_complete_line( bool );
void refresh_line( HINT_ACTION = HINT_ACTION::REGENERATE );
void render( char32_t );
void render( HINT_ACTION );
int handle_hints( HINT_ACTION );
void set_color( Replxx::Color );
int context_length( void );
void clear( void );
void repaint( void );
bool is_word_break_character( char32_t ) const;
void dynamicRefresh(Prompt& pi, char32_t* buf32, int len, int pos);
char const* finalize_input( char const* );
void clear_self_to_end_of_screen( void );
typedef struct {
int index;
bool error;
} paren_info_t;
paren_info_t matching_paren( void );
};

}

#endif


+ 179
- 0
contrib/replxx/src/unicodestring.hxx View File

@@ -0,0 +1,179 @@
#ifndef REPLXX_UNICODESTRING_HXX_INCLUDED
#define REPLXX_UNICODESTRING_HXX_INCLUDED

#include <vector>
#include <cstring>

#include "conversion.hxx"

namespace replxx {

class UnicodeString {
public:
typedef std::vector<char32_t> data_buffer_t;
typedef data_buffer_t::const_iterator const_iterator;
typedef data_buffer_t::iterator iterator;
private:
data_buffer_t _data;
public:
UnicodeString()
: _data() {
}

explicit UnicodeString( std::string const& src )
: _data() {
assign( src );
}

explicit UnicodeString( char const* src )
: _data() {
assign( src );
}

explicit UnicodeString( char8_t const* src )
: UnicodeString( reinterpret_cast<const char*>( src ) ) {
}

explicit UnicodeString( char32_t const* src )
: _data() {
int len( 0 );
while ( src[len] != 0 ) {
++ len;
}
_data.assign( src, src + len );
}

explicit UnicodeString( char32_t const* src, int len )
: _data() {
_data.assign( src, src + len );
}

explicit UnicodeString( int len )
: _data() {
_data.resize( len );
}

UnicodeString& assign( std::string const& str_ ) {
_data.resize( str_.length() );
int len( 0 );
copyString8to32( _data.data(), str_.length(), len, str_.c_str() );
_data.resize( len );
return *this;
}

UnicodeString& assign( char const* str_ ) {
size_t byteCount( strlen( str_ ) );
_data.resize( byteCount );
int len( 0 );
copyString8to32( _data.data(), byteCount, len, str_ );
_data.resize( len );
return *this;
}

UnicodeString& assign( UnicodeString const& other_ ) {
_data = other_._data;
return *this;
}

explicit UnicodeString( UnicodeString const& ) = default;
UnicodeString& operator = ( UnicodeString const& ) = default;
UnicodeString( UnicodeString&& ) = default;
UnicodeString& operator = ( UnicodeString&& ) = default;
bool operator == ( UnicodeString const& other_ ) const {
return ( _data == other_._data );
}

bool operator != ( UnicodeString const& other_ ) const {
return ( _data != other_._data );
}

UnicodeString& append( UnicodeString const& other ) {
_data.insert( _data.end(), other._data.begin(), other._data.end() );
return *this;
}

UnicodeString& append( char32_t const* src, int len ) {
_data.insert( _data.end(), src, src + len );
return *this;
}

UnicodeString& insert( int pos_, UnicodeString const& str_, int offset_, int len_ ) {
_data.insert( _data.begin() + pos_, str_._data.begin() + offset_, str_._data.begin() + offset_ + len_ );
return *this;
}

UnicodeString& insert( int pos_, char32_t c_ ) {
_data.insert( _data.begin() + pos_, c_ );
return *this;
}

UnicodeString& erase( int pos_ ) {
_data.erase( _data.begin() + pos_ );
return *this;
}

UnicodeString& erase( int pos_, int len_ ) {
_data.erase( _data.begin() + pos_, _data.begin() + pos_ + len_ );
return *this;
}

char32_t const* get() const {
return _data.data();
}

char32_t* get() {
return _data.data();
}

int length() const {
return static_cast<int>( _data.size() );
}

void clear( void ) {
_data.clear();
}

const char32_t& operator[]( size_t pos ) const {
return _data[pos];
}

char32_t& operator[]( size_t pos ) {
return _data[pos];
}

bool starts_with( data_buffer_t::const_iterator first_, data_buffer_t::const_iterator last_ ) const {
return (
( std::distance( first_, last_ ) <= length() )
&& ( std::equal( first_, last_, _data.begin() ) )
);
}

bool is_empty( void ) const {
return ( _data.size() == 0 );
}

void swap( UnicodeString& other_ ) {
_data.swap( other_._data );
}

const_iterator begin( void ) const {
return ( _data.begin() );
}

const_iterator end( void ) const {
return ( _data.end() );
}

iterator begin( void ) {
return ( _data.begin() );
}

iterator end( void ) {
return ( _data.end() );
}
};

}

#endif


+ 71
- 0
contrib/replxx/src/utf8string.hxx View File

@@ -0,0 +1,71 @@
#ifndef REPLXX_UTF8STRING_HXX_INCLUDED
#define REPLXX_UTF8STRING_HXX_INCLUDED

#include <memory>

#include "unicodestring.hxx"

namespace replxx {

class Utf8String {
private:
typedef std::unique_ptr<char[]> buffer_t;
buffer_t _data;
int _bufSize;
public:
Utf8String( void )
: _data()
, _bufSize( 0 ) {
}
explicit Utf8String( UnicodeString const& src )
: _data()
, _bufSize( 0 ) {
assign( src, src.length() );
}

Utf8String( UnicodeString const& src_, int len_ )
: _data()
, _bufSize( 0 ) {
assign( src_, len_ );
}

void assign( UnicodeString const& str_ ) {
assign( str_, str_.length() );
}

void assign( UnicodeString const& str_, int len_ ) {
int len( len_ * 4 );
realloc( len );
copyString32to8( _data.get(), len, str_.get(), len_ );
}

void assign( std::string const& str_ ) {
realloc( str_.length() );
strncpy( _data.get(), str_.c_str(), str_.length() );
}

char const* get() const {
return _data.get();
}

private:
void realloc( int reqLen ) {
if ( ( reqLen + 1 ) > _bufSize ) {
_bufSize = 1;
while ( ( reqLen + 1 ) > _bufSize ) {
_bufSize *= 2;
}
_data.reset( new char[_bufSize] );
memset( _data.get(), 0, _bufSize );
}
_data[reqLen] = 0;
return;
}
Utf8String(const Utf8String&) = delete;
Utf8String& operator=(const Utf8String&) = delete;
};

}

#endif


+ 152
- 0
contrib/replxx/src/util.cxx View File

@@ -0,0 +1,152 @@
#include <cstdlib>
#include <cstring>
#include <wctype.h>

#include "util.hxx"

namespace replxx {

int mk_wcwidth( char32_t );

/**
* Recompute widths of all characters in a char32_t buffer
* @param text - input buffer of Unicode characters
* @param widths - output buffer of character widths
* @param charCount - number of characters in buffer
*/
void recompute_character_widths( char32_t const* text, char* widths, int charCount ) {
for (int i = 0; i < charCount; ++i) {
widths[i] = mk_wcwidth(text[i]);
}
}

/**
* Calculate a new screen position given a starting position, screen width and
* character count
* @param x - initial x position (zero-based)
* @param y - initial y position (zero-based)
* @param screenColumns - screen column count
* @param charCount - character positions to advance
* @param xOut - returned x position (zero-based)
* @param yOut - returned y position (zero-based)
*/
void calculate_screen_position(
int x, int y, int screenColumns,
int charCount, int& xOut, int& yOut
) {
xOut = x;
yOut = y;
int charsRemaining = charCount;
while ( charsRemaining > 0 ) {
int charsThisRow = ( ( x + charsRemaining ) < screenColumns )
? charsRemaining
: screenColumns - x;
xOut = x + charsThisRow;
yOut = y;
charsRemaining -= charsThisRow;
x = 0;
++ y;
}
if ( xOut == screenColumns ) { // we have to special-case line wrap
xOut = 0;
++ yOut;
}
}

/**
* Calculate a column width using mk_wcswidth()
* @param buf32 - text to calculate
* @param len - length of text to calculate
*/
int calculate_displayed_length( char32_t const* buf32_, int size_ ) {
int len( 0 );
for ( int i( 0 ); i < size_; ++ i ) {
char32_t c( buf32_[i] );
if ( c == '\033' ) {
int escStart( i );
++ i;
if ( ( i < size_ ) && ( buf32_[i] != '[' ) ) {
i = escStart;
++ len;
continue;
}
++ i;
for ( ; i < size_; ++ i ) {
c = buf32_[i];
if ( ( c != ';' ) && ( ( c < '0' ) || ( c > '9' ) ) ) {
break;
}
}
if ( ( i < size_ ) && ( buf32_[i] == 'm' ) ) {
continue;
}
i = escStart;
len += 2;
} else if ( is_control_code( c ) ) {
len += 2;
} else {
int wcw( mk_wcwidth( c ) );
if ( wcw < 0 ) {
len = -1;
break;
}
len += wcw;
}
}
return ( len );
}

char const* ansi_color( Replxx::Color color_ ) {
static char const reset[] = "\033[0m";
static char const black[] = "\033[0;22;30m";
static char const red[] = "\033[0;22;31m";
static char const green[] = "\033[0;22;32m";
static char const brown[] = "\033[0;22;33m";
static char const blue[] = "\033[0;22;34m";
static char const magenta[] = "\033[0;22;35m";
static char const cyan[] = "\033[0;22;36m";
static char const lightgray[] = "\033[0;22;37m";

#ifdef _WIN32
static bool const has256colorDefault( true );
#else
static bool const has256colorDefault( false );
#endif
static char const* TERM( getenv( "TERM" ) );
static bool const has256color( TERM ? ( strstr( TERM, "256" ) != nullptr ) : has256colorDefault );
static char const* gray = has256color ? "\033[0;1;90m" : "\033[0;1;30m";
static char const* brightred = has256color ? "\033[0;1;91m" : "\033[0;1;31m";
static char const* brightgreen = has256color ? "\033[0;1;92m" : "\033[0;1;32m";
static char const* yellow = has256color ? "\033[0;1;93m" : "\033[0;1;33m";
static char const* brightblue = has256color ? "\033[0;1;94m" : "\033[0;1;34m";
static char const* brightmagenta = has256color ? "\033[0;1;95m" : "\033[0;1;35m";
static char const* brightcyan = has256color ? "\033[0;1;96m" : "\033[0;1;36m";
static char const* white = has256color ? "\033[0;1;97m" : "\033[0;1;37m";
static char const error[] = "\033[101;1;33m";

char const* code( reset );
switch ( color_ ) {
case Replxx::Color::BLACK: code = black; break;
case Replxx::Color::RED: code = red; break;
case Replxx::Color::GREEN: code = green; break;
case Replxx::Color::BROWN: code = brown; break;
case Replxx::Color::BLUE: code = blue; break;
case Replxx::Color::MAGENTA: code = magenta; break;
case Replxx::Color::CYAN: code = cyan; break;
case Replxx::Color::LIGHTGRAY: code = lightgray; break;
case Replxx::Color::GRAY: code = gray; break;
case Replxx::Color::BRIGHTRED: code = brightred; break;
case Replxx::Color::BRIGHTGREEN: code = brightgreen; break;
case Replxx::Color::YELLOW: code = yellow; break;
case Replxx::Color::BRIGHTBLUE: code = brightblue; break;
case Replxx::Color::BRIGHTMAGENTA: code = brightmagenta; break;
case Replxx::Color::BRIGHTCYAN: code = brightcyan; break;
case Replxx::Color::WHITE: code = white; break;
case Replxx::Color::ERROR: code = error; break;
case Replxx::Color::DEFAULT: code = reset; break;
}
return ( code );
}

}


+ 21
- 0
contrib/replxx/src/util.hxx View File

@@ -0,0 +1,21 @@
#ifndef REPLXX_UTIL_HXX_INCLUDED
#define REPLXX_UTIL_HXX_INCLUDED 1

#include "replxx.hxx"

namespace replxx {

inline bool is_control_code(char32_t testChar) {
return (testChar < ' ') || // C0 controls
(testChar >= 0x7F && testChar <= 0x9F); // DEL and C1 controls
}

void recompute_character_widths( char32_t const* text, char* widths, int charCount );
void calculate_screen_position( int x, int y, int screenColumns, int charCount, int& xOut, int& yOut );
int calculate_displayed_length( char32_t const* buf32, int size );
char const* ansi_color( Replxx::Color );

}

#endif


+ 296
- 0
contrib/replxx/src/wcwidth.cpp View File

@@ -0,0 +1,296 @@
/*
* This is an implementation of wcwidth() and wcswidth() (defined in
* IEEE Std 1002.1-2001) for Unicode.
*
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
*
* In fixed-width output devices, Latin characters all occupy a single
* "cell" position of equal width, whereas ideographic CJK characters
* occupy two such cells. Interoperability between terminal-line
* applications and (teletype-style) character terminals using the
* UTF-8 encoding requires agreement on which character should advance
* the cursor by how many cell positions. No established formal
* standards exist at present on which Unicode character shall occupy
* how many cell positions on character terminals. These routines are
* a first attempt of defining such behavior based on simple rules
* applied to data provided by the Unicode Consortium.
*
* For some graphical characters, the Unicode standard explicitly
* defines a character-cell width via the definition of the East Asian
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
* In all these cases, there is no ambiguity about which width a
* terminal shall use. For characters in the East Asian Ambiguous (A)
* class, the width choice depends purely on a preference of backward
* compatibility with either historic CJK or Western practice.
* Choosing single-width for these characters is easy to justify as
* the appropriate long-term solution, as the CJK practice of
* displaying these characters as double-width comes from historic
* implementation simplicity (8-bit encoded characters were displayed
* single-width and 16-bit ones double-width, even for Greek,
* Cyrillic, etc.) and not any typographic considerations.
*
* Much less clear is the choice of width for the Not East Asian
* (Neutral) class. Existing practice does not dictate a width for any
* of these characters. It would nevertheless make sense
* typographically to allocate two character cells to characters such
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
* represented adequately with a single-width glyph. The following
* routines at present merely assign a single-cell width to all
* neutral characters, in the interest of simplicity. This is not
* entirely satisfactory and should be reconsidered before
* establishing a formal standard in this area. At the moment, the
* decision which Not East Asian (Neutral) characters should be
* represented by double-width glyphs cannot yet be answered by
* applying a simple rule from the Unicode database content. Setting
* up a proper standard for the behavior of UTF-8 character terminals
* will require a careful analysis not only of each Unicode character,
* but also of each presentation form, something the author of these
* routines has avoided to do so far.
*
* http://www.unicode.org/unicode/reports/tr11/
*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/

#include <wchar.h>
#include <string>
#include <memory>

namespace replxx {

struct interval {
char32_t first;
char32_t last;
};

/* auxiliary function for binary search in interval table */
static int bisearch(char32_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;

if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
max = mid - 1;
else
return 1;
}

return 0;
}


/* The following two functions define the column width of an ISO 10646
* character as follows:
*
* - The null character (U+0000) has a column width of 0.
*
* - Other C0/C1 control characters and DEL will lead to a return
* value of -1.
*
* - Non-spacing and enclosing combining characters (general
* category code Mn or Me in the Unicode database) have a
* column width of 0.
*
* - SOFT HYPHEN (U+00AD) has a column width of 1.
*
* - Other format characters (general category code Cf in the Unicode
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
*
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
* have a column width of 0.
*
* - Spacing characters in the East Asian Wide (W) or East Asian
* Full-width (F) category as defined in Unicode Technical
* Report #11 have a column width of 2.
*
* - All remaining characters (including all printable
* ISO 8859-1 and WGL4 characters, Unicode control characters,
* etc.) have a column width of 1.
*
* This implementation assumes that wchar_t characters are encoded
* in ISO 10646.
*/

int mk_is_wide_char(char32_t ucs) {
static const struct interval wide[] = {
{0x1100, 0x115f}, {0x231a, 0x231b}, {0x2329, 0x232a},
{0x23e9, 0x23ec}, {0x23f0, 0x23f0}, {0x23f3, 0x23f3},
{0x25fd, 0x25fe}, {0x2614, 0x2615}, {0x2648, 0x2653},
{0x267f, 0x267f}, {0x2693, 0x2693}, {0x26a1, 0x26a1},
{0x26aa, 0x26ab}, {0x26bd, 0x26be}, {0x26c4, 0x26c5},
{0x26ce, 0x26ce}, {0x26d4, 0x26d4}, {0x26ea, 0x26ea},
{0x26f2, 0x26f3}, {0x26f5, 0x26f5}, {0x26fa, 0x26fa},
{0x26fd, 0x26fd}, {0x2705, 0x2705}, {0x270a, 0x270b},
{0x2728, 0x2728}, {0x274c, 0x274c}, {0x274e, 0x274e},
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
{0x27b0, 0x27b0}, {0x27bf, 0x27bf}, {0x2b1b, 0x2b1c},
{0x2b50, 0x2b50}, {0x2b55, 0x2b55}, {0x2e80, 0x2fdf},
{0x2ff0, 0x303e}, {0x3040, 0x3247}, {0x3250, 0x4dbf},
{0x4e00, 0xa4cf}, {0xa960, 0xa97f}, {0xac00, 0xd7a3},
{0xf900, 0xfaff}, {0xfe10, 0xfe19}, {0xfe30, 0xfe6f},
{0xff01, 0xff60}, {0xffe0, 0xffe6}, {0x16fe0, 0x16fe1},
{0x17000, 0x18aff}, {0x1b000, 0x1b12f}, {0x1b170, 0x1b2ff},
{0x1f004, 0x1f004}, {0x1f0cf, 0x1f0cf}, {0x1f18e, 0x1f18e},
{0x1f191, 0x1f19a}, {0x1f200, 0x1f202}, {0x1f210, 0x1f23b},
{0x1f240, 0x1f248}, {0x1f250, 0x1f251}, {0x1f260, 0x1f265},
{0x1f300, 0x1f320}, {0x1f32d, 0x1f335}, {0x1f337, 0x1f37c},
{0x1f37e, 0x1f393}, {0x1f3a0, 0x1f3ca}, {0x1f3cf, 0x1f3d3},
{0x1f3e0, 0x1f3f0}, {0x1f3f4, 0x1f3f4}, {0x1f3f8, 0x1f43e},
{0x1f440, 0x1f440}, {0x1f442, 0x1f4fc}, {0x1f4ff, 0x1f53d},
{0x1f54b, 0x1f54e}, {0x1f550, 0x1f567}, {0x1f57a, 0x1f57a},
{0x1f595, 0x1f596}, {0x1f5a4, 0x1f5a4}, {0x1f5fb, 0x1f64f},
{0x1f680, 0x1f6c5}, {0x1f6cc, 0x1f6cc}, {0x1f6d0, 0x1f6d2},
{0x1f6eb, 0x1f6ec}, {0x1f6f4, 0x1f6f8}, {0x1f910, 0x1f93e},
{0x1f940, 0x1f94c}, {0x1f950, 0x1f96b}, {0x1f980, 0x1f997},
{0x1f9c0, 0x1f9c0}, {0x1f9d0, 0x1f9e6}, {0x20000, 0x2fffd},
{0x30000, 0x3fffd},
};

if ( bisearch(ucs, wide, sizeof(wide) / sizeof(struct interval) - 1) ) {
return 1;
}

return 0;
}

int mk_wcwidth(char32_t ucs) {
/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{0x00ad, 0x00ad}, {0x0300, 0x036f}, {0x0483, 0x0489},
{0x0591, 0x05bd}, {0x05bf, 0x05bf}, {0x05c1, 0x05c2},
{0x05c4, 0x05c5}, {0x05c7, 0x05c7}, {0x0610, 0x061a},
{0x061c, 0x061c}, {0x064b, 0x065f}, {0x0670, 0x0670},
{0x06d6, 0x06dc}, {0x06df, 0x06e4}, {0x06e7, 0x06e8},
{0x06ea, 0x06ed}, {0x0711, 0x0711}, {0x0730, 0x074a},
{0x07a6, 0x07b0}, {0x07eb, 0x07f3}, {0x0816, 0x0819},
{0x081b, 0x0823}, {0x0825, 0x0827}, {0x0829, 0x082d},
{0x0859, 0x085b}, {0x08d4, 0x08e1}, {0x08e3, 0x0902},
{0x093a, 0x093a}, {0x093c, 0x093c}, {0x0941, 0x0948},
{0x094d, 0x094d}, {0x0951, 0x0957}, {0x0962, 0x0963},
{0x0981, 0x0981}, {0x09bc, 0x09bc}, {0x09c1, 0x09c4},
{0x09cd, 0x09cd}, {0x09e2, 0x09e3}, {0x0a01, 0x0a02},
{0x0a3c, 0x0a3c}, {0x0a41, 0x0a42}, {0x0a47, 0x0a48},
{0x0a4b, 0x0a4d}, {0x0a51, 0x0a51}, {0x0a70, 0x0a71},
{0x0a75, 0x0a75}, {0x0a81, 0x0a82}, {0x0abc, 0x0abc},
{0x0ac1, 0x0ac5}, {0x0ac7, 0x0ac8}, {0x0acd, 0x0acd},
{0x0ae2, 0x0ae3}, {0x0afa, 0x0aff}, {0x0b01, 0x0b01},
{0x0b3c, 0x0b3c}, {0x0b3f, 0x0b3f}, {0x0b41, 0x0b44},
{0x0b4d, 0x0b4d}, {0x0b56, 0x0b56}, {0x0b62, 0x0b63},
{0x0b82, 0x0b82}, {0x0bc0, 0x0bc0}, {0x0bcd, 0x0bcd},
{0x0c00, 0x0c00}, {0x0c3e, 0x0c40}, {0x0c46, 0x0c48},
{0x0c4a, 0x0c4d}, {0x0c55, 0x0c56}, {0x0c62, 0x0c63},
{0x0c81, 0x0c81}, {0x0cbc, 0x0cbc}, {0x0cbf, 0x0cbf},
{0x0cc6, 0x0cc6}, {0x0ccc, 0x0ccd}, {0x0ce2, 0x0ce3},
{0x0d00, 0x0d01}, {0x0d3b, 0x0d3c}, {0x0d41, 0x0d44},
{0x0d4d, 0x0d4d}, {0x0d62, 0x0d63}, {0x0dca, 0x0dca},
{0x0dd2, 0x0dd4}, {0x0dd6, 0x0dd6}, {0x0e31, 0x0e31},
{0x0e34, 0x0e3a}, {0x0e47, 0x0e4e}, {0x0eb1, 0x0eb1},
{0x0eb4, 0x0eb9}, {0x0ebb, 0x0ebc}, {0x0ec8, 0x0ecd},
{0x0f18, 0x0f19}, {0x0f35, 0x0f35}, {0x0f37, 0x0f37},
{0x0f39, 0x0f39}, {0x0f71, 0x0f7e}, {0x0f80, 0x0f84},
{0x0f86, 0x0f87}, {0x0f8d, 0x0f97}, {0x0f99, 0x0fbc},
{0x0fc6, 0x0fc6}, {0x102d, 0x1030}, {0x1032, 0x1037},
{0x1039, 0x103a}, {0x103d, 0x103e}, {0x1058, 0x1059},
{0x105e, 0x1060}, {0x1071, 0x1074}, {0x1082, 0x1082},
{0x1085, 0x1086}, {0x108d, 0x108d}, {0x109d, 0x109d},
{0x1160, 0x11ff}, {0x135d, 0x135f}, {0x1712, 0x1714},
{0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773},
{0x17b4, 0x17b5}, {0x17b7, 0x17bd}, {0x17c6, 0x17c6},
{0x17c9, 0x17d3}, {0x17dd, 0x17dd}, {0x180b, 0x180e},
{0x1885, 0x1886}, {0x18a9, 0x18a9}, {0x1920, 0x1922},
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193b},
{0x1a17, 0x1a18}, {0x1a1b, 0x1a1b}, {0x1a56, 0x1a56},
{0x1a58, 0x1a5e}, {0x1a60, 0x1a60}, {0x1a62, 0x1a62},
{0x1a65, 0x1a6c}, {0x1a73, 0x1a7c}, {0x1a7f, 0x1a7f},
{0x1ab0, 0x1abe}, {0x1b00, 0x1b03}, {0x1b34, 0x1b34},
{0x1b36, 0x1b3a}, {0x1b3c, 0x1b3c}, {0x1b42, 0x1b42},
{0x1b6b, 0x1b73}, {0x1b80, 0x1b81}, {0x1ba2, 0x1ba5},
{0x1ba8, 0x1ba9}, {0x1bab, 0x1bad}, {0x1be6, 0x1be6},
{0x1be8, 0x1be9}, {0x1bed, 0x1bed}, {0x1bef, 0x1bf1},
{0x1c2c, 0x1c33}, {0x1c36, 0x1c37}, {0x1cd0, 0x1cd2},
{0x1cd4, 0x1ce0}, {0x1ce2, 0x1ce8}, {0x1ced, 0x1ced},
{0x1cf4, 0x1cf4}, {0x1cf8, 0x1cf9}, {0x1dc0, 0x1df9},
{0x1dfb, 0x1dff}, {0x200b, 0x200f}, {0x202a, 0x202e},
{0x2060, 0x2064}, {0x2066, 0x206f}, {0x20d0, 0x20f0},
{0x2cef, 0x2cf1}, {0x2d7f, 0x2d7f}, {0x2de0, 0x2dff},
{0x302a, 0x302d}, {0x3099, 0x309a}, {0xa66f, 0xa672},
{0xa674, 0xa67d}, {0xa69e, 0xa69f}, {0xa6f0, 0xa6f1},
{0xa802, 0xa802}, {0xa806, 0xa806}, {0xa80b, 0xa80b},
{0xa825, 0xa826}, {0xa8c4, 0xa8c5}, {0xa8e0, 0xa8f1},
{0xa926, 0xa92d}, {0xa947, 0xa951}, {0xa980, 0xa982},
{0xa9b3, 0xa9b3}, {0xa9b6, 0xa9b9}, {0xa9bc, 0xa9bc},
{0xa9e5, 0xa9e5}, {0xaa29, 0xaa2e}, {0xaa31, 0xaa32},
{0xaa35, 0xaa36}, {0xaa43, 0xaa43}, {0xaa4c, 0xaa4c},
{0xaa7c, 0xaa7c}, {0xaab0, 0xaab0}, {0xaab2, 0xaab4},
{0xaab7, 0xaab8}, {0xaabe, 0xaabf}, {0xaac1, 0xaac1},
{0xaaec, 0xaaed}, {0xaaf6, 0xaaf6}, {0xabe5, 0xabe5},
{0xabe8, 0xabe8}, {0xabed, 0xabed}, {0xfb1e, 0xfb1e},
{0xfe00, 0xfe0f}, {0xfe20, 0xfe2f}, {0xfeff, 0xfeff},
{0xfff9, 0xfffb}, {0x101fd, 0x101fd}, {0x102e0, 0x102e0},
{0x10376, 0x1037a}, {0x10a01, 0x10a03}, {0x10a05, 0x10a06},
{0x10a0c, 0x10a0f}, {0x10a38, 0x10a3a}, {0x10a3f, 0x10a3f},
{0x10ae5, 0x10ae6}, {0x11001, 0x11001}, {0x11038, 0x11046},
{0x1107f, 0x11081}, {0x110b3, 0x110b6}, {0x110b9, 0x110ba},
{0x11100, 0x11102}, {0x11127, 0x1112b}, {0x1112d, 0x11134},
{0x11173, 0x11173}, {0x11180, 0x11181}, {0x111b6, 0x111be},
{0x111ca, 0x111cc}, {0x1122f, 0x11231}, {0x11234, 0x11234},
{0x11236, 0x11237}, {0x1123e, 0x1123e}, {0x112df, 0x112df},
{0x112e3, 0x112ea}, {0x11300, 0x11301}, {0x1133c, 0x1133c},
{0x11340, 0x11340}, {0x11366, 0x1136c}, {0x11370, 0x11374},
{0x11438, 0x1143f}, {0x11442, 0x11444}, {0x11446, 0x11446},
{0x114b3, 0x114b8}, {0x114ba, 0x114ba}, {0x114bf, 0x114c0},
{0x114c2, 0x114c3}, {0x115b2, 0x115b5}, {0x115bc, 0x115bd},
{0x115bf, 0x115c0}, {0x115dc, 0x115dd}, {0x11633, 0x1163a},
{0x1163d, 0x1163d}, {0x1163f, 0x11640}, {0x116ab, 0x116ab},
{0x116ad, 0x116ad}, {0x116b0, 0x116b5}, {0x116b7, 0x116b7},
{0x1171d, 0x1171f}, {0x11722, 0x11725}, {0x11727, 0x1172b},
{0x11a01, 0x11a06}, {0x11a09, 0x11a0a}, {0x11a33, 0x11a38},
{0x11a3b, 0x11a3e}, {0x11a47, 0x11a47}, {0x11a51, 0x11a56},
{0x11a59, 0x11a5b}, {0x11a8a, 0x11a96}, {0x11a98, 0x11a99},
{0x11c30, 0x11c36}, {0x11c38, 0x11c3d}, {0x11c3f, 0x11c3f},
{0x11c92, 0x11ca7}, {0x11caa, 0x11cb0}, {0x11cb2, 0x11cb3},
{0x11cb5, 0x11cb6}, {0x11d31, 0x11d36}, {0x11d3a, 0x11d3a},
{0x11d3c, 0x11d3d}, {0x11d3f, 0x11d45}, {0x11d47, 0x11d47},
{0x16af0, 0x16af4}, {0x16b30, 0x16b36}, {0x16f8f, 0x16f92},
{0x1bc9d, 0x1bc9e}, {0x1bca0, 0x1bca3}, {0x1d167, 0x1d169},
{0x1d173, 0x1d182}, {0x1d185, 0x1d18b}, {0x1d1aa, 0x1d1ad},
{0x1d242, 0x1d244}, {0x1da00, 0x1da36}, {0x1da3b, 0x1da6c},
{0x1da75, 0x1da75}, {0x1da84, 0x1da84}, {0x1da9b, 0x1da9f},
{0x1daa1, 0x1daaf}, {0x1e000, 0x1e006}, {0x1e008, 0x1e018},
{0x1e01b, 0x1e021}, {0x1e023, 0x1e024}, {0x1e026, 0x1e02a},
{0x1e8d0, 0x1e8d6}, {0x1e944, 0x1e94a}, {0xe0001, 0xe0001},
{0xe0020, 0xe007f}, {0xe0100, 0xe01ef},
};

/* test for 8-bit control characters */
if ( ucs == 0 ) {
return 0;
}
if ( ( ucs < 32 ) || ( ( ucs >= 0x7f ) && ( ucs < 0xa0 ) ) ) {
return -1;
}

/* binary search in table of non-spacing characters */
if ( bisearch( ucs, combining, sizeof( combining ) / sizeof( struct interval ) - 1 ) ) {
return 0;
}

/* if we arrive here, ucs is not a combining or C0/C1 control character */
return ( mk_is_wide_char( ucs ) ? 2 : 1 );
}

}


+ 158
- 0
contrib/replxx/src/windows.cxx View File

@@ -0,0 +1,158 @@
#ifdef _WIN32

#include <iostream>

#include "windows.hxx"
#include "conversion.hxx"
#include "io.hxx"

#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
static DWORD const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
#endif

using namespace std;

namespace replxx {

WinAttributes WIN_ATTR;

template<typename T>
T* HandleEsc(T* p, T* end) {
if (*p == '[') {
int code = 0;

int thisBackground( WIN_ATTR._defaultBackground );
for (++p; p < end; ++p) {
char32_t c = *p;

if ('0' <= c && c <= '9') {
code = code * 10 + (c - '0');
} else if (c == 'm' || c == ';') {
switch (code) {
case 0:
WIN_ATTR._consoleAttribute = WIN_ATTR._defaultAttribute;
WIN_ATTR._consoleColor = WIN_ATTR._defaultColor | thisBackground;
break;
case 1: // BOLD
case 5: // BLINK
WIN_ATTR._consoleAttribute = (WIN_ATTR._defaultAttribute ^ FOREGROUND_INTENSITY) & INTENSITY;
break;
case 22:
WIN_ATTR._consoleAttribute = WIN_ATTR._defaultAttribute;
break;
case 30:
case 90:
WIN_ATTR._consoleColor = thisBackground;
break;
case 31:
case 91:
WIN_ATTR._consoleColor = FOREGROUND_RED | thisBackground;
break;
case 32:
case 92:
WIN_ATTR._consoleColor = FOREGROUND_GREEN | thisBackground;
break;
case 33:
case 93:
WIN_ATTR._consoleColor = FOREGROUND_RED | FOREGROUND_GREEN | thisBackground;
break;
case 34:
case 94:
WIN_ATTR._consoleColor = FOREGROUND_BLUE | thisBackground;
break;
case 35:
case 95:
WIN_ATTR._consoleColor = FOREGROUND_BLUE | FOREGROUND_RED | thisBackground;
break;
case 36:
case 96:
WIN_ATTR._consoleColor = FOREGROUND_BLUE | FOREGROUND_GREEN | thisBackground;
break;
case 37:
case 97:
WIN_ATTR._consoleColor = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | thisBackground;
break;
case 101:
thisBackground = BACKGROUND_RED;
break;
}

if ( ( code >= 90 ) && ( code <= 97 ) ) {
WIN_ATTR._consoleAttribute = (WIN_ATTR._defaultAttribute ^ FOREGROUND_INTENSITY) & INTENSITY;
}

code = 0;
}

if (*p == 'm') {
++p;
break;
}
}
} else {
++p;
}

auto handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(
handle,
WIN_ATTR._consoleAttribute | WIN_ATTR._consoleColor
);

return p;
}

int win_write( char const* str_, int size_ ) {
int count( 0 );
DWORD currentMode( 0 );
HANDLE consoleOut( GetStdHandle( STD_OUTPUT_HANDLE ) );
if ( tty::out && GetConsoleMode( consoleOut, &currentMode ) ) {
UINT inputCodePage( GetConsoleCP() );
UINT outputCodePage( GetConsoleOutputCP() );
SetConsoleCP( 65001 );
SetConsoleOutputCP( 65001 );
DWORD nWritten( 0 );
if ( SetConsoleMode( consoleOut, currentMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ) {
WriteConsoleA( consoleOut, str_, size_, &nWritten, nullptr );
count = nWritten;
SetConsoleMode( consoleOut, currentMode );
} else {
char const* s( str_ );
char const* e( str_ + size_ );
while ( str_ < e ) {
if ( *str_ == 27 ) {
if ( s < str_ ) {
int toWrite( str_ - s );
WriteConsoleA( consoleOut, s, static_cast<DWORD>( toWrite ), &nWritten, nullptr );
count += nWritten;
if ( nWritten != toWrite ) {
s = str_ = nullptr;
break;
}
}
s = HandleEsc( str_ + 1, e );
int escaped( s - str_);
count += escaped;
str_ = s;
} else {
++ str_;
}
}

if ( s < str_ ) {
WriteConsoleA( consoleOut, s, static_cast<DWORD>( str_ - s ), &nWritten, nullptr );
count += nWritten;
}
}
SetConsoleCP( inputCodePage );
SetConsoleOutputCP( outputCodePage );
} else {
count = _write( 1, str_, size_ );
}
return ( count );
}

}

#endif


+ 44
- 0
contrib/replxx/src/windows.hxx View File

@@ -0,0 +1,44 @@
#ifndef REPLXX_WINDOWS_HXX_INCLUDED
#define REPLXX_WINDOWS_HXX_INCLUDED 1

#include <conio.h>
#include <windows.h>
#include <io.h>

namespace replxx {

static const int FOREGROUND_WHITE =
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
static const int BACKGROUND_WHITE =
BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
static const int INTENSITY = FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;

class WinAttributes {
public:
WinAttributes() {
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
_defaultAttribute = info.wAttributes & INTENSITY;
_defaultColor = info.wAttributes & FOREGROUND_WHITE;
_defaultBackground = info.wAttributes & BACKGROUND_WHITE;

_consoleAttribute = _defaultAttribute;
_consoleColor = _defaultColor | _defaultBackground;
}

public:
int _defaultAttribute;
int _defaultColor;
int _defaultBackground;

int _consoleAttribute;
int _consoleColor;
};

int win_write( char const*, int );

extern WinAttributes WIN_ATTR;

}

#endif

Loading…
Cancel
Save