├── .gitignore ├── .gitmodules ├── CMake └── Mathematica │ ├── FindMathematica.cmake │ ├── FindMathematicaDocumentationBuild.cmake.in │ ├── FindMathematicaTestDriver.cmd │ └── FindMathematicaTestDriver.sh ├── CMakeLists.txt ├── LICENSE-FindMathematica.txt ├── LICENSE-linenoise.txt ├── LICENSE-popl.txt ├── LICENSE.txt ├── README.md ├── configure.py ├── dep └── linenoise-ng │ ├── .clang-format │ ├── .gitignore │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── appveyor.yml │ ├── include │ └── linenoise.h │ ├── src │ ├── ConvertUTF.cpp │ ├── ConvertUTF.h │ ├── linenoise.cpp │ └── wcwidth.cpp │ └── tst │ └── example.c └── src ├── config.h.in ├── main.cpp ├── mlbridge.cpp ├── mlbridge.h └── popl.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | wstp.h 3 | mathlink.h 4 | build 5 | .idea 6 | MathLine 7 | *.tar.gz 8 | spkg 9 | 10 | ## CMake generated files. 11 | *.cbp 12 | CMakeCache.txt 13 | CMakeFiles 14 | CMakeScripts 15 | Testing 16 | Makefile 17 | cmake_install.cmake 18 | install_manifest.txt 19 | compile_commands.json 20 | CTestTestfile.cmake 21 | cmake-build-debug 22 | 23 | ## C/C++ gitignore from https://github.com/github/gitignore. 24 | 25 | # Prerequisites 26 | *.d 27 | 28 | # Object files 29 | *.o 30 | *.ko 31 | *.obj 32 | *.elf 33 | *.slo 34 | 35 | # Linker output 36 | *.ilk 37 | *.map 38 | *.exp 39 | 40 | # Precompiled Headers 41 | *.gch 42 | *.pch 43 | 44 | # Libraries 45 | *.lib 46 | *.a 47 | *.la 48 | *.lai 49 | *.lo 50 | 51 | # Shared objects (inc. Windows DLLs) 52 | *.dll 53 | *.so 54 | *.so.* 55 | *.dylib 56 | 57 | # Executables 58 | *.exe 59 | *.out 60 | *.app 61 | *.i*86 62 | *.x86_64 63 | *.hex 64 | 65 | # Debug files 66 | *.dSYM/ 67 | *.su 68 | *.idb 69 | *.pdb 70 | 71 | # Kernel Module Compile Results 72 | *.mod* 73 | *.cmd 74 | modules.order 75 | Module.symvers 76 | Mkfile.old 77 | dkms.conf 78 | config.h 79 | 80 | # include *.cmd in CMake folder 81 | !CMake/Mathematica/*.cmd -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".\\dep\\linenoise-ng\\"] 2 | path = .\\dep\\linenoise-ng\\ 3 | url = https://github.com/arangodb/linenoise-ng.git 4 | -------------------------------------------------------------------------------- /CMake/Mathematica/FindMathematicaDocumentationBuild.cmake.in: -------------------------------------------------------------------------------- 1 | # FindMathematica @Mathematica_CMAKE_MODULE_VERSION@ documentation build script 2 | 3 | # JAVACMD is an environment variable that points to the full path to the Java runtime executable 4 | # used by the Apache Ant wrapper scripts 5 | set (ENV{JAVACMD} "@_option_JAVACMD@") 6 | 7 | set (Mathematica_KERNEL_EXECUTABLE "@Mathematica_KERNEL_EXECUTABLE@") 8 | set (Mathematica_ANT_EXECUTABLE "@Mathematica_ANT_EXECUTABLE@") 9 | set (Mathematica_JLink_PACKAGE_DIR "@Mathematica_JLink_PACKAGE_DIR@") 10 | set (Mathematica_DocumentationBuild_PACKAGE_DIR "@Mathematica_DocumentationBuild_PACKAGE_DIR@") 11 | set (Mathematica_DEBUG "@Mathematica_DEBUG@") 12 | 13 | set (DOCU_INPUT_DIRECTORY "@_option_INPUT_DIRECTORY@") 14 | set (DOCU_OUTPUT_DIRECTORY "@_option_OUTPUT_DIRECTORY@") 15 | set (DOCU_TYPE "@_option_DOCUMENTATION_TYPE@") 16 | set (DOCU_LANGUAGE "@_option_LANGUAGE@") 17 | set (DOCU_APP_NAME "@_option_APPLICATION_NAME@") 18 | set (CHECK_TIMESTAMPS "@_option_CHECK_TIMESTAMPS@") 19 | 20 | function(_to_native_path _inPath _outPathVariable) 21 | if (CYGWIN) 22 | execute_process( 23 | COMMAND cygpath "--mixed" "${_inPath}" TIMEOUT 5 24 | OUTPUT_VARIABLE ${_outPathVariable} OUTPUT_STRIP_TRAILING_WHITESPACE) 25 | elseif (CMAKE_HOST_WIN32) 26 | string (REPLACE "/" "\\" ${_outPathVariable} "${_inPath}") 27 | else() 28 | # use CMake path literally 29 | set (${_outPathVariable} "${_inPath}") 30 | endif() 31 | set (${_outPathVariable} "${${_outPathVariable}}" PARENT_SCOPE) 32 | endfunction() 33 | 34 | set (_buildDocu TRUE) 35 | # handle CHECK_TIMESTAMPS option 36 | if (CHECK_TIMESTAMPS AND EXISTS "${DOCU_OUTPUT_DIRECTORY}") 37 | if (DOCU_TYPE MATCHES "[Nn]otebook") 38 | file (GLOB_RECURSE _docuSourceNBs RELATIVE "${DOCU_INPUT_DIRECTORY}" "${DOCU_INPUT_DIRECTORY}/*.nb") 39 | file (GLOB_RECURSE _docuBinaryNBs RELATIVE "${DOCU_OUTPUT_DIRECTORY}" "${DOCU_OUTPUT_DIRECTORY}/*.nb") 40 | if (_docuBinaryNBs) 41 | set (_docuNBs ${_docuSourceNBs} ${_docuBinaryNBs}) 42 | list (REMOVE_DUPLICATES _docuNBs) 43 | set (_docuNBTrigger "") 44 | foreach (_docuNB ${_docuNBs}) 45 | if ("${DOCU_INPUT_DIRECTORY}/${_docuNB}" IS_NEWER_THAN "${DOCU_OUTPUT_DIRECTORY}/${_docuNB}") 46 | list (APPEND _docuNBTrigger "${_docuNB}") 47 | list (LENGTH _docuNBTrigger _len) 48 | if (_len GREATER 10) 49 | # stop if many out-of-date files have been found 50 | break() 51 | endif() 52 | endif() 53 | endforeach() 54 | if (_docuNBTrigger) 55 | message (STATUS "Out-of-date ${DOCU_APP_NAME} Mathematica documentation notebooks: ${_docuNBTrigger}") 56 | else() 57 | message (STATUS "Built ${DOCU_APP_NAME} Mathematica ${DOCU_TYPE} documentation is up-to-date") 58 | set (_buildDocu FALSE) 59 | endif() 60 | endif() 61 | elseif (DOCU_TYPE MATCHES "HTML") 62 | file (GLOB_RECURSE _docuSourceNBs RELATIVE "${DOCU_INPUT_DIRECTORY}" "${DOCU_INPUT_DIRECTORY}/*.nb") 63 | file (GLOB_RECURSE _docuBinaryHTMLs RELATIVE "${DOCU_OUTPUT_DIRECTORY}" "${DOCU_OUTPUT_DIRECTORY}/*.html") 64 | if (_docuBinaryHTMLs) 65 | set (_docuNBTrigger "") 66 | foreach (_docuNB ${_docuSourceNBs}) 67 | get_filename_component(_docuBaseName "${_docuNB}" NAME_WE) 68 | string (REGEX MATCHALL "[^;]+/${_docuBaseName}\\.html" _docuHTMLNames "${_docuBinaryHTMLs}") 69 | if (_docuHTMLNames) 70 | foreach (_docuHTMLName ${_docuHTMLNames}) 71 | if ("${DOCU_INPUT_DIRECTORY}/${_docuNB}" IS_NEWER_THAN "${DOCU_OUTPUT_DIRECTORY}/${_docuHTMLName}") 72 | list (APPEND _docuNBTrigger "${_docuNB}") 73 | break() 74 | endif() 75 | endforeach() 76 | else() 77 | # no corresponding HTML document 78 | list (APPEND _docuNBTrigger "${_docuNB}") 79 | endif() 80 | list (LENGTH _docuNBTrigger _len) 81 | if (_len GREATER 10) 82 | # stop if many out-of-date files have been found 83 | break() 84 | endif() 85 | endforeach() 86 | if (_docuNBTrigger) 87 | message (STATUS "Out-of-date ${DOCU_APP_NAME} Mathematica documentation notebooks: ${_docuNBTrigger}") 88 | else() 89 | message (STATUS "Built ${DOCU_APP_NAME} Mathematica ${DOCU_TYPE} documentation is up-to-date") 90 | set (_buildDocu FALSE) 91 | endif() 92 | endif() 93 | endif() 94 | endif() 95 | 96 | if (_buildDocu) 97 | # clean previously built documentation files 98 | file (REMOVE_RECURSE "${DOCU_OUTPUT_DIRECTORY}") 99 | file (MAKE_DIRECTORY "${DOCU_OUTPUT_DIRECTORY}") 100 | get_filename_component(_appPath "${Mathematica_DocumentationBuild_PACKAGE_DIR}" DIRECTORY) 101 | string (TOLOWER "${DOCU_TYPE}.xml" _buildFileName) 102 | set (_buildFile "${Mathematica_DocumentationBuild_PACKAGE_DIR}/SystemFiles/ant/Build/${_buildFileName}") 103 | _to_native_path ("${Mathematica_ANT_EXECUTABLE}" _antExecutableNative) 104 | _to_native_path ("${_buildFile}" _buildFileNative) 105 | _to_native_path ("${_appPath}" _appPathNative) 106 | _to_native_path ("${Mathematica_KERNEL_EXECUTABLE}" _kernelExecutableNative) 107 | _to_native_path ("${Mathematica_JLink_PACKAGE_DIR}" _jlinkPathNative) 108 | _to_native_path ("${DOCU_INPUT_DIRECTORY}" _inputDirNative) 109 | _to_native_path ("${DOCU_OUTPUT_DIRECTORY}" _outputDirNative) 110 | set (_cmd COMMAND "${_antExecutableNative}") 111 | list (APPEND _cmd "-buildfile" "${_buildFileNative}") 112 | list (APPEND _cmd "-DappPath=${_appPathNative}") 113 | list (APPEND _cmd "-Dapp.name=${DOCU_APP_NAME}") 114 | list (APPEND _cmd "-DmathExe=${_kernelExecutableNative}") 115 | list (APPEND _cmd "-Djlinkpath=${_jlinkPathNative}") 116 | list (APPEND _cmd "-DinputDir=${_inputDirNative}") 117 | list (APPEND _cmd "-DoutputDir=${_outputDirNative}") 118 | list (APPEND _cmd "-Dlanguage=${DOCU_LANGUAGE}") 119 | list (APPEND _cmd "-DincludeLinkTrails=False") 120 | list (APPEND _cmd "-Dlocal=True") 121 | list (APPEND _cmd "-DcompleteHTMLQ=True") 122 | if (Mathematica_DEBUG) 123 | list (APPEND _cmd "-Ddebug=True") 124 | else() 125 | list (APPEND _cmd "-Ddebug=False") 126 | endif() 127 | list (APPEND _cmd RESULT_VARIABLE _result) 128 | if (Mathematica_DEBUG) 129 | message (STATUS "execute_process: ${_cmd}") 130 | endif() 131 | execute_process(${_cmd}) 132 | message(STATUS "${DOCU_APP_NAME} ${DOCU_TYPE} generation result=${_result}") 133 | endif() 134 | -------------------------------------------------------------------------------- /CMake/Mathematica/FindMathematicaTestDriver.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem FindMathematica test driver script for Windows 3 | 4 | setlocal enabledelayedexpansion 5 | 6 | rem echo !CMDCMDLINE! 7 | rem echo !PATH! 8 | 9 | set "TEST_NAME=%~1" 10 | set "TEST_CONFIGURATION=%~2" 11 | set "TEST_INPUT_OPTION=%~3" 12 | if "!TEST_INPUT_OPTION!" == "input" ( 13 | set "TEST_INPUT=%~4" 14 | set "TEST_EXECUTABLE=%~5" 15 | shift 16 | shift 17 | shift 18 | shift 19 | shift 20 | shift 21 | ) else if "!TEST_INPUT_OPTION!" == "inputfile" ( 22 | set "TEST_INPUT_FILE=%~4" 23 | set "TEST_EXECUTABLE=%~5" 24 | shift 25 | shift 26 | shift 27 | shift 28 | shift 29 | shift 30 | ) else ( 31 | set "TEST_EXECUTABLE=%~4" 32 | shift 33 | shift 34 | shift 35 | shift 36 | shift 37 | ) 38 | 39 | if "!TEST_INPUT_OPTION!" == "input" ( 40 | echo !TEST_INPUT! | "!TEST_EXECUTABLE!" %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 41 | ) else if "!TEST_INPUT_OPTION!" == "inputfile" ( 42 | "!TEST_EXECUTABLE!" < "!TEST_INPUT_FILE!" %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 43 | ) else ( 44 | "!TEST_EXECUTABLE!" %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 45 | ) 46 | -------------------------------------------------------------------------------- /CMake/Mathematica/FindMathematicaTestDriver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # FindMathematica test driver script for UNIX systems 3 | 4 | #logger -- $# "$@" 5 | #logger -- LD_LIBRARY_PATH=$LD_LIBRARY_PATH 6 | #logger -- DYLD_FRAMEWORK_PATH=$DYLD_FRAMEWORK_PATH 7 | #logger -- DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH 8 | 9 | export TEST_NAME=$1 10 | export TEST_CONFIGURATION=$2 11 | export TEST_INPUT_OPTION=$3 12 | 13 | if [ "$TEST_INPUT_OPTION" = "input" ] 14 | then 15 | export TEST_INPUT=$4 16 | export TEST_EXECUTABLE=$5 17 | elif [ "$TEST_INPUT_OPTION" = "inputfile" ] 18 | then 19 | export TEST_INPUT_FILE=$4 20 | export TEST_EXECUTABLE=$5 21 | else 22 | export TEST_EXECUTABLE=$4 23 | fi 24 | 25 | if [ "$OSTYPE" = "cygwin" ] 26 | then 27 | # make sure that executable has the right format under Cygwin 28 | export TEST_EXECUTABLE="`/usr/bin/cygpath --unix \"$TEST_EXECUTABLE\"`" 29 | fi 30 | 31 | if [ "$TEST_INPUT_OPTION" = "input" ] 32 | then 33 | echo "$TEST_INPUT" | exec "$TEST_EXECUTABLE" "${@:6}" 34 | elif [ "$TEST_INPUT_OPTION" = "inputfile" ] 35 | then 36 | exec < "$TEST_INPUT_FILE" "$TEST_EXECUTABLE" "${@:6}" 37 | else 38 | exec "$TEST_EXECUTABLE" "${@:5}" 39 | fi 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Create the project. 2 | cmake_minimum_required(VERSION 2.6) 3 | project(mathline) 4 | add_executable(mathline ${CMAKE_SOURCE_DIR}/src/main.cpp ${CMAKE_SOURCE_DIR}/src/mlbridge.cpp) 5 | include_directories("${CMAKE_SOURCE_DIR}/src" "${CMAKE_SOURCE_DIR}/dep/linenoise-ng/include" "${CMAKE_SOURCE_DIR}/build") 6 | 7 | # Enable C++11 extensions. 8 | target_compile_features(mathline PRIVATE cxx_constexpr) 9 | 10 | #### FindMathematica #### 11 | cmake_policy(SET CMP0012 OLD) # Silences warnings from FindMathematica 12 | cmake_policy(SET CMP0011 NEW) # Silences warnings from FindMathematica 13 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake/Mathematica" ${CMAKE_MODULE_PATH}) 14 | # Dynamically linking to MathLink introduces a nondeterministic malloc error upon 15 | # exit. Statically linking seems to solve this problem. 16 | set(Mathematica_USE_LIBCXX_LIBRARIES OFF) 17 | set(Mathematica_USE_STATIC_LIBRARIES ON) 18 | find_package(Mathematica COMPONENTS WSTP MathLink) 19 | # The following is only relevant for linking dynamically to MathLink on Mac OS X. 20 | # We record it here for future reference. 21 | # Fix MathLink shared library references under Mac OS X: 22 | # Mathematica_ABSOLUTIZE_LIBRARY_DEPENDENCIES(mathline) 23 | 24 | # Choose between WSTP and MathLink. 25 | if(Mathematica_WSTP_FOUND) 26 | set(WSTP true) 27 | set(ML_PREFIX WS) 28 | include_directories(${Mathematica_WSTP_INCLUDE_DIR}) 29 | target_link_libraries(mathline ${Mathematica_WSTP_LIBRARY}) 30 | message("WSTP library is: ${Mathematica_WSTP_LIBRARY}") 31 | elseif(Mathematica_MathLink_FOUND) 32 | set(WSTP false) 33 | set(ML_PREFIX ML) 34 | include_directories(${Mathematica_MathLink_INCLUDE_DIR}) 35 | target_link_libraries(mathline ${Mathematica_MathLink_LIBRARY}) 36 | endif() 37 | 38 | # Compile linenose-ng library and link to it 39 | add_subdirectory(dep/linenoise-ng) 40 | target_link_libraries(mathline linenoise) 41 | 42 | # Link to the correct stdlib. 43 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 44 | # We're on macOS X. 45 | # TODO: Prior to version 10.4, use -lstdc++ in EXTRA_LIBS to link against libstdc++ instead of libc++. 46 | # MathLink requires we link to CoreFoundation 47 | find_library(COREFOUNDATION_LIBRARY CoreFoundation) 48 | target_link_libraries(mathline ${COREFOUNDATION_LIBRARY}) 49 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 50 | # Using GCC. MathLink requires additional libraries. 51 | target_link_libraries(mathline m pthread c++ dl) 52 | endif() 53 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 54 | # We're on Microsoft Windows. 55 | 56 | else() 57 | # The libuuid library is special. On (at least) Ubuntu libuuid.so isn't 58 | # symlinked to libuuid.so.1 if libuuid-dev isn't installed, so ld can't 59 | # find -luuid. 60 | find_library(UUID_LIBRARY REQUIRED NAMES uuid libuuid.so.1) 61 | target_link_libraries(mathline m pthread rt stdc++ dl ${UUID_LIBRARY}) 62 | endif() 63 | 64 | # Configure a header file to pass some of the CMake settings 65 | # to the source code 66 | configure_file("${CMAKE_SOURCE_DIR}/src/config.h.in" "${CMAKE_SOURCE_DIR}/build/config.h") 67 | 68 | # Installation 69 | install(TARGETS mathline DESTINATION bin) -------------------------------------------------------------------------------- /LICENSE-FindMathematica.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2015 Sascha Kratky 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LICENSE-linenoise.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014, Salvatore Sanfilippo 2 | Copyright (c) 2010-2013, Pieter Noordhuis 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /LICENSE-popl.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2016 Johannes Pohl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Robert Jacobson 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | MathLine is a **Math**ematica command **Line** front end to the Mathematica kernel. The front end has feature parity with the textual front end that ships with Mathematica but provides a stable getline or readline-like interface that won't break between Mathematica releases. 4 | 5 | # Features 6 | * Optionally bypass the Main Loop. 7 | * Configurable prompts. 8 | * Readline-like text input (uses [linenoise](http://github.com/antirez/linenoise)), which means command history and emacs-style editing. A more primitive `std:getline` interface is also available via a command line option. 9 | * Automatic collection of postscript code produced by the kernel for the output of graphics, say, using xpdf. 10 | * Open source, BSD licensed. 11 | 12 | # Usage 13 | 14 | ## Preparing your environment 15 | MathLine needs access to a Mathematica kernel (Wolfram kernel). By default, MathLine assumes that a kernel can be started on a command line with: 16 | 17 | `$ math` 18 | 19 | If that does nothing, the easiest thing to do is create a bash script named `math` in `/usr/bin` (if there isn't one already). **Do not create a symlink instead.** Put the following in the file `/usr/bin/math` and make it executable: 20 | 21 | ``` 22 | #!/bin/bash 23 | 24 | /path/to/WolframKernel $@ 25 | ``` 26 | The kernel might be called `MathKernel` or something else on different systems. On my system (macOS Sierra, Mathematica v11.01), the path to the kernel is: 27 | 28 | `/Applications/Mathematica.app/Contents/MacOS/WolframKernel` 29 | 30 | If you want MathLine to start a kernel different from the default ("`math -wstp`"), use the `--linkname` option: 31 | 32 | `$ mathline --linkname "/path/to/kernel -wstp"` 33 | 34 | ### WSTP or MathLink? 35 | 36 | WSTP stands for Wolfram Symbolic Transfer Protocol. In Mathematica v10 Wolfram changed the name of MathLink to WSTP. *WSTP is identical to MathLink except for the name.* Change all occurrences of "wstp" to "mathlink" in the above if you are trying to use a kernel older than v10. Newer versions of Mathematica continue to ship both MathLink and WSTP to maintain backwards compatibility. MathLine selects the appropriate one at compile time. 37 | 38 | ## Using MathLine 39 | 40 | MathLine will use reasonable defaults if invoked without arguments. 41 | 42 | `$ mathline [--option arg ]*` 43 | 44 | If you get, 45 | ``` 46 | Could not connect to Mathematica. Check that "math -wstp" works from a command line. 47 | ``` 48 | then read the section "Preparing your environment" above. 49 | 50 | Option| Description 51 | ----------------------------|--------------------------- 52 | ` --mainloop arg (=1)` |Boolean. Whether or not to use the kernel's Main Loop which keeps track of session history with `In[#]` and `Out[#]` variables. Defaults to true. 53 | `--prompt arg ` |String. The prompt presented to the user for input. When inoutstrings is true, this is typically the empty string. 54 | `--inoutstrings arg (=1)` |Boolean. Whether or not to print the `In[#]:=` and `Out[#]=` strings. When mainloop is false this option does nothing. Defaults to true. 55 | `--linkname arg` |String. The call string to set up the link. The default works on *nix systems on which math is in the path and runnable. Defaults to `"math -wstp"`. 56 | ` --linkmode arg` |String. The WSTP/MathLink link mode. The default launches a new kernel which is almost certainly what you want. It should be possible, however, to take over an already existing kernel, though this has not been tested. Defaults to "launch". 57 | ` --usegetline arg (=0)` |Boolean. If set to false, we use readline-like input with command history and emacs-style editing capability. If set to true, we use a simplified getline input with limited editing capability. Try setting this to true if MathLine connects to a kernel but doesn't give a correct prompt. Defaults to false. 58 | `--maxhistory arg (=10)` |Integer (nonnegative). The maximum number of lines to keep in the input history. Defaults to 10. 59 | `--help` |Produce help message. 60 | 61 | ## Using with Python’s `Pexpect` and Similar Usages 62 | 63 | MathLine was motivated by the need for a stable Mathematica frontend that could be used with pipes, for example, to communicate with a Mathematica kernel using `pexpect` in Python. Here is a minimal example: 64 | 65 | ```python 66 | import sys 67 | import re 68 | import pexpect 69 | 70 | # Configure how to start MathLine. We ask MathLine to use the easier-to-recognize 71 | # prompt '>>> ' and to not output 'In[n]:= ' and 'Out[n]='. 72 | mathline_path = "/path/to/mathline" 73 | command = mathline_path + ' -g true -i false -p ">>> "' 74 | prompt = '>>> ' 75 | 76 | # Spawn the MathLine instance. 77 | child = pexpect.spawn(command, encoding='utf-8', timeout=5, echo=False) 78 | 79 | # Expect the prompt '>>> '. 80 | child.expect(prompt) 81 | # Send a Mathematica expression to evaluate. In this case, we check the equivalence 82 | # of two representations of a Taxicab Number. 83 | child.sendline('17492496^3 + 26590452^3 == 18289922^3 + 26224366^3') 84 | # Read everything up to the next prompt. 85 | child.expect(prompt) 86 | # Display the output. 87 | print(child.before.strip()) 88 | 89 | # Tell MathLine and Mathematica to exit. 90 | child.sendline('Exit[]') 91 | # Close the pexpect link. 92 | child.close() 93 | ``` 94 | 95 | One could also use `pexpect`’s built-in `REPLWrapper` object as in the following. 96 | 97 | ```python 98 | import sys 99 | import re 100 | import pexpect 101 | # This time we also import REPLWrapper. 102 | from pexpect.replwrap import REPLWrapper 103 | 104 | # Same as before, changing the prompt to '>>> '. 105 | mathline_path = "/Users/rjacobson/Development/mathline/cmake-build-debug/mathline" 106 | command = mathline_path + ' -g true -i false -p ">>> "' 107 | prompt = '>>> ' 108 | 109 | # Spawn the MathLine instance. 110 | child = pexpect.spawn(command, encoding='utf-8', timeout=5, echo=False) 111 | # Create a `REPLWrapper` object that looks for our predefined prompt. 112 | wl_repl = REPLWrapper(child, '>>> ', prompt_change=None, continuation_prompt=u' ') 113 | 114 | # Give the REPL an expression to evaluate and print the result. 115 | print(wl_repl.run_command('2^32-17').strip()) 116 | 117 | child.sendline('Exit[]') 118 | child.close() 119 | ``` 120 | 121 | Note that with `REPLWrapper`, changing the prompt is not optional, as `REPLWrapper` uses `expect_exact()`, meaning it does not accept regular expressions. In other words, we can’t use a prompt that changes, as `In[n]:= ` does. Also, in both examples we simultaneously *disabled* the in-out strings—a requirement for `REPLWrapper` but merely optional in the first example. 122 | 123 | ## Dependencies 124 | 125 | **Compile Time:** CMake is used to locate the WSTP/MathLink header and library and is the recommended way to build MathLine. Those users without cmake on their system will have to either use the included Python script to generate a make file or determine the magic build incantation themselves. 126 | 127 | **Run Time:** You will need a functional installation of Mathematica. To build MathLine, your Mathematica installation must include the WSTP or MathLink DeveloperKit. (By default, Mathematica installs both.) Of course the Mathematica kernel (Wolfram kernel) is necessary at runtime for MathLine to be of any use. 128 | 129 | # Building 130 | Make sure that Mathematica is installed on the build system (including the WSTP/MathLink DeveloperKit). 131 | 132 | ## Building with CMake 133 | If you have CMake on your system, the project includes CMake files that do all of the hard stuff for you. On most systems just execute the following in the root directory of the project: 134 | 135 | ``` 136 | mkdir build 137 | cd build 138 | cmake ../ 139 | make 140 | ``` 141 | CMake will select WSTP or MathLink automatically. If all goes well you should have a MathLine binary sitting in the build directory. To install it: 142 | 143 | ```make install``` 144 | 145 | ## Building with GenMakefile.py 146 | 147 | This is not supported or recommended. If you don't have CMake, there is an included Python script that will generate an appropriate Makefile for you, automatically selecting WSTP or MathLink depending on your Mathematica version. This script assumes that Mathematica is installed on your computer with a command line interface that runs when you give the `math` command at the terminal. (See the section "Preparing your environment" above.) To use the script, do the following: 148 | 149 | ``` 150 | mkdir build 151 | ./GenMakefile.py -runMake 152 | ``` 153 | If the build is successful, the MathLine binary will be in the build directory. If you omit the `-runMake`, the script will output the Makefile without running `make`. 154 | 155 | ## Building manually 156 | You can also build it the hard way. (Adapt these instructions in the obvious way for MathLink.) Build it like any other WSTP C/C++ program: Make sure `wstp.h` (shipped with Mathematica) is available in your include search path and link the binary to Mathematica's WSTP library (`libWSTPi4.a` on my system). 157 | 158 | # Missing Features 159 | For those looking to enhance this code, here are some possibilities. These are features that the textual interface doesn't have but should. Someone should contribute the code. 160 | 161 | * Implement scripting, i.e., `mathline --script file.m` executes the code in file.m and exits. (Easy.) 162 | * Intelligently display output formatted with ToString. (Easy, but I can't figure it out, so hard?) 163 | * The code makes reasonable choices for when to print newline characters. However, this should probably be configurable, say, by printing "preprint" and "postprint" strings around each printed string. (Easy.) 164 | * Implement autocompletion of names and contexts using something similar to this code which is similar to what JMath uses:
165 | `NamesAndContexts[x_String] := Sort[Join[ Select[Contexts[], StringMatchQ[#, x] &], Names[x]]]` 166 | (Medium.) 167 | * Implement autocompletion of filenames at, e.g., "<<". (Medium.) 168 | * Implement window size change. (Easy.) 169 | 170 | The last few items in the list above are features of [JMath](http://robotics.caltech.edu/~radford/jmath/), another free and open source (GPL) textual front end to Mathematica written by Jim Radford. 171 | 172 | # Authors and License 173 | 174 | Author(s): Robert Jacobson \ 175 | 176 | License: BSD license. See the file LICENSE.txt for details. Note that this license applies ONLY to the MathLine source code. At compile time MathLine links against the MathLink/WSTP library which use and distribution is governed by a separate non-free license. It is your responsibility to ensure your use of the linked MathLine binary is in compliance with the MathLink/WSTP license. 177 | 178 | This software includes linenoise ([http://github.com/antirez/linenoise](http://github.com/antirez/linenoise)), a readline-like library by Salvatore Sanfilippo and Pieter Noordhuis released under the BSD license. See LICENSE-linenoise.txt for more information. 179 | 180 | This software includes FindMathematica ([https://github.com/sakra/FindMathematica](https://github.com/sakra/FindMathematica)), a CMake module by Sascha Kratky that tries to find a Mathematica installation and provides CMake functions for Mathematica's C/C++ interface. FindMathematica is licensed under the MIT license. See LICENSE-FindMathematica.txt for more information. 181 | 182 | This software includes popl ([https://github.com/badaix/popl](https://github.com/badaix/popl)), a c++ command line arguments parser by Johannes Pohl licensed under the MIT license. See LICENSE-popl.txt for more information. 183 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import shutil 4 | import os 5 | import sys 6 | import subprocess 7 | import platform 8 | import argparse as ap 9 | 10 | makefile_template = """# This makefile was autogenerated by configure.py. 11 | 12 | CPP=%(cpp_compiler)s 13 | CC=%(c_compiler)s 14 | CFLAGS=%(cflags)s 15 | LDFLAGS=%(lflags)s 16 | 17 | CPPSOURCES=%(cpp_sources)s 18 | CSOURCES=%(c_sources)s 19 | 20 | OBJECTS=$(CPPSOURCES:.cpp=.o) $(CSOURCES:.c=.o) 21 | EXECUTABLE=%(binary)s 22 | 23 | all: $(CPPSOURCES) $(CSOURCES) $(EXECUTABLE) 24 | 25 | $(EXECUTABLE): $(OBJECTS) 26 | \t$(CPP) $(LDFLAGS) $(OBJECTS) -o $@ 27 | 28 | .cpp.o: 29 | \t$(CPP) $(CFLAGS) $< -o $@ 30 | 31 | .c.o: 32 | \t$(CC) $(CFLAGS) $< -o $@ 33 | 34 | clean: 35 | \t./configure.py -clean 36 | 37 | install: 38 | \tinstall -c $(EXECUTABLE) "%(prefix)s/bin" 39 | """ 40 | 41 | cpp_compiler = "g++" # Files with a .cpp extension are compiled as c++ files. 42 | c_compiler = "gcc" # Files with a .c extension are compiled as c files. 43 | 44 | tmp_path = "build" # We build everything in a build directory. 45 | output_path = "build" # If empty, we use the tmp_path directory. 46 | output_file = "MathLine" 47 | source_files = [ 48 | "main.cpp", 49 | "mlbridge.cpp", 50 | "linenoise.c" 51 | ] 52 | 53 | # Absolute path to Mathematica installation directory. We determine this 54 | # directory by evaluating $InstallationDirectory in Mathematica. 55 | mma_install_path = "" # "/Applications/Mathematica.app/Contents" 56 | 57 | # Set-able via command-line argument --mma_path="/path/to/MathKernel" 58 | mma_kernel = "math" 59 | 60 | # The path to the wstp developer files, relative to mma_installation_path. 61 | # According to: https://reference.wolfram.com/language/tutorial/WSTPDeveloperGuide-Unix.html 62 | # "The WSTP Developer Kit (WSDK) is located in the directory 63 | # $InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/$SystemID within your Wolfram 64 | # System directory." 65 | wstp_path = "/SystemFiles/Links/WSTP/DeveloperKit" 66 | mathlink_path = "/SystemFiles/Links/MathLink/DeveloperKit" 67 | # mma_link_path either the wstp_path or the mathlink_path, depending on Mathematica version 68 | mma_link_path = "" 69 | 70 | # Are we using the WSTP library (True) or the MathLink library (False)? 71 | wstp = True 72 | 73 | # The path to wstp.h, relative to mma_installation_path. 74 | include_path = "" # E.g. "/MacOSX-x86-64/CompilerAdditions" 75 | 76 | # The path to libWSTPi4.a, relative to mma_installation_path. 77 | library_path = "" # E.g. "/MacOSX-x86-64/CompilerAdditions/AlternativeLibraries" 78 | 79 | # The libraries that the final binary should be linked with. These library 80 | # names will be prefixed with the usual "-l" when passed to the linker. 81 | link_libs = ["boost_program_options"] 82 | 83 | # Any other flags to pass to the linker. 84 | other_link_flags = [] 85 | 86 | # Any other flags to pass to the compilers. Why not -O2? 87 | other_compiler_flags = ["-O2", "-c", "-w"] 88 | 89 | # List of object files. This is populated using the list of source files, 90 | # so there is no need to specify them explicitly. 91 | object_files = [] 92 | 93 | prefix="/usr/local" 94 | 95 | args = None 96 | 97 | def parseProgramOptions(): 98 | """ 99 | Parses the program options supplied to this script. 100 | """ 101 | global other_compiler_flags, other_link_flags, mma_kernel, prefix, args 102 | 103 | 104 | parser = ap.ArgumentParser() 105 | parser.add_argument('--prefix', help='The prefix of the installation path.' ) 106 | parser.add_argument('--libdir', help='The library directory.' ) 107 | parser.add_argument('--includedir', help='The include directory.' ) 108 | parser.add_argument('--mma_path', 109 | help='The path to the math script or MathKernel binary. If "math" is in your path then there is not need for this option.' ) 110 | parser.add_argument('--clean', action='store_true', 111 | help='Remove all files the build process created and exit.' ) 112 | parser.add_argument('--make', action='store_true', 113 | help='Run make after generating the Makefile.' ) 114 | args = parser.parse_args() 115 | 116 | if args.prefix is not None: 117 | prefix = args.prefix 118 | if args.libdir is not None: 119 | other_link_flags.append('"-L' + args.libdir + '"') 120 | 121 | if args.includedir is not None: 122 | other_compiler_flags.append('"-I' + args.includedir + '"') 123 | else: 124 | other_compiler_flags.append('"-I' + prefix + '/include"') 125 | 126 | if args.mma_path is not None: 127 | mma_kernel = args.mma_path 128 | 129 | # Convenience function, does what it says. 130 | def deleteFileIfExists(file): 131 | try: 132 | os.remove(file) 133 | except: 134 | pass 135 | 136 | # Wipes everything this script has created. 137 | def clean(remove_binary = False): 138 | global tmp_path 139 | 140 | # Remove any .m files that may be around. 141 | deleteFileIfExists(tmp_path + "/" + "MMACommand.m") 142 | 143 | # Delete the generated Makefile. 144 | deleteFileIfExists("Makefile") 145 | 146 | # Remove the binary if we were asked to. 147 | if remove_binary: 148 | deleteFileIfExists(tmp_path + "/" + output_file) 149 | 150 | # Delete object files and the copies of the source files we made. 151 | for file in source_files: 152 | short_name = "" 153 | if file[-2:] == ".c": 154 | short_name = file[:-2] 155 | else: 156 | short_name = file[:-4] 157 | # Remove the object file. 158 | deleteFileIfExists(tmp_path + "/" + short_name + ".o") 159 | # Remove the source file... 160 | deleteFileIfExists(tmp_path + "/" + file) 161 | # ...and header. 162 | deleteFileIfExists(tmp_path + "/" + short_name + ".h") 163 | 164 | # Gets the directory of the Mathematica installation, sets the values of 165 | # mma_install_path, wstp_path, library_path, and include_path. 166 | def getMathematicaPaths(): 167 | global mma_install_path, wstp_path, mathlink_path, mma_link_path, library_path, include_path, link_libs, wstp 168 | 169 | # Determine Mathematica's installation path. 170 | mma_install_path = getMathematicaValue("$InstallationDirectory") 171 | 172 | # Determine the Mathematica version. 173 | mma_version = float(getMathematicaValue("$VersionNumber")) 174 | if mma_version >= 10: 175 | mma_link_path = wstp_path 176 | wstp = True 177 | else: 178 | mma_link_path = mathlink_path 179 | wstp = False 180 | 181 | 182 | # Determine the static libraries to link the final binary with. 183 | platform_system = platform.system() 184 | if platform_system == "linux2": 185 | if platform.processor() == "i386" or platform.processor() == "i586" or platform.processor() == "i686": 186 | if wstp: 187 | link_libs.append("WSTP32i4") 188 | else: 189 | link_libs.append("ML32i4") 190 | link_libs.extend(["stdc++", "dl", "uuid"]) 191 | else: 192 | if wstp: 193 | link_libs.append("WSTP64i4") 194 | else: 195 | link_libs.append("ML64i4") 196 | link_libs.extend(["stdc++", "dl", "uuid"]) 197 | elif platform_system == "Darwin": 198 | if wstp: 199 | link_libs.append("WSTPi4") 200 | else: 201 | link_libs.append("MLi4") 202 | other_link_flags.append("-framework CoreFoundation") 203 | elif platform_system == "win32": 204 | # Untested and likely won't work. 205 | if wstp: 206 | link_libs.extend(["wstp32i4s", "Ws2_32.lib", "Rpcrt4.lib"]) 207 | else: 208 | link_libs.extend(["ml32i4s", "Ws2_32.lib", "Rpcrt4.lib"]) 209 | elif platform_system == "win64": 210 | # Untested and likely won't work. 211 | if wstp: 212 | link_libs.extend(["wstp64i4s", "Ws2_32.lib", "Rpcrt4.lib"]) 213 | else: 214 | link_libs.extend(["ml64i4s", "Ws2_32.lib", "Rpcrt4.lib"]) 215 | 216 | # Find the include and library paths. 217 | # According to: https://reference.wolfram.com/language/tutorial/WSTPDeveloperGuide-Unix.html 218 | # "The WSTP Developer Kit (WSDK) is located in the directory 219 | # $InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/$SystemID within your Wolfram 220 | # System directory." 221 | system_id = getMathematicaValue("$SystemID") 222 | if platform_system == "Darwin": 223 | # This AlternativeLibraries directory contains versions of the WSTP 224 | # libraries compiled with -stdlib=libc++ compiler flags. These libraries 225 | # exist to link programs that need compatibility with Apple's newer 226 | # libc++ C++ library. 227 | library_path = "/%s/CompilerAdditions/AlternativeLibraries" % system_id 228 | else: 229 | library_path = "/%s/CompilerAdditions" % system_id 230 | 231 | include_path = "/%s/CompilerAdditions" % system_id 232 | 233 | def getMathematicaValue(value): 234 | mma_command = "Print[%s]" % value 235 | script_path = tmp_path + "/MMACommand.m" 236 | 237 | # Delete the current script if it already exists. 238 | try: 239 | os.remove(script_path) 240 | except OSError: 241 | pass 242 | # Write out the command. 243 | with open(script_path, 'w') as f: 244 | f.write(mma_command) 245 | 246 | run_command = 'math -script "%s"' % script_path 247 | 248 | result = subprocess.check_output(run_command, shell=True) 249 | # Remove the terminal newline. 250 | return result[:-1] 251 | 252 | def main(): 253 | global output_path, mma_install_path 254 | 255 | parseProgramOptions() 256 | # Check if we are just asked to clean. 257 | if args.clean is True: 258 | clean(remove_binary=True) 259 | return 260 | 261 | # Set up build directory. 262 | print "Build directory: " + tmp_path 263 | if not os.path.exists(tmp_path): 264 | os.makedirs(tmp_path) 265 | 266 | # Now we ask Mathematica where it is installed. 267 | try: 268 | getMathematicaPaths() 269 | except subprocess.CalledProcessError as e: 270 | print "Return code: %d" % e.returncode 271 | print "Is '%s' runnable from a command line?" % mma_kernel 272 | clean() 273 | return 274 | 275 | print "Mathematica directory: " + mma_install_path 276 | # print "Library Path: " + library_path 277 | # print "Include Path: " + include_path 278 | 279 | # Copy sources to the tmp_path directory. 280 | if wstp: 281 | print "Using WSTP." 282 | shutil.copy("src/WSTP/mlbridge.cpp", tmp_path + "/mlbridge.cpp") 283 | shutil.copy("src/WSTP/mlbridge.h", tmp_path + "/mlbridge.h") 284 | else: 285 | print "Using MathLink." 286 | shutil.copy("src/MathLink/mlbridge.cpp", tmp_path + "/mlbridge.cpp") 287 | shutil.copy("src/MathLink/mlbridge.h", tmp_path + "/mlbridge.h") 288 | files = [f for f in os.listdir("src") if os.path.isfile("src/" + f)] 289 | for file_name in files: 290 | shutil.copy("src/" + file_name, tmp_path + "/" + file_name) 291 | 292 | # Set the location of the final binary after build (not install). 293 | if output_path == "": 294 | output_path = tmp_path 295 | 296 | # Compute the include path and library path. 297 | other_compiler_flags.append('"-I' + mma_install_path + mma_link_path + include_path + '"') 298 | other_link_flags.append('"-L' + mma_install_path + mma_link_path + library_path + '"') 299 | other_link_flags.append(' '.join(["-l" + lib for lib in link_libs]) ) 300 | 301 | # Construct the makefile. 302 | cflags = " ".join(other_compiler_flags) 303 | lflags = " ".join(other_link_flags) 304 | cpp_sources = " ".join([tmp_path + "/" + file for file in source_files if file[-4:]==".cpp"]) 305 | c_sources = " ".join([tmp_path + "/" + file for file in source_files if file[-2:]==".c"]) 306 | binary = output_path + "/" + output_file 307 | template_dict = { "cflags": cflags, 308 | "lflags": lflags, 309 | "cpp_sources": cpp_sources, 310 | "c_sources": c_sources, 311 | "cpp_compiler": cpp_compiler, 312 | "c_compiler": c_compiler, 313 | "binary": binary, 314 | "prefix": prefix 315 | } 316 | # print template_dict 317 | # print makefile_template 318 | 319 | makefile = makefile_template % template_dict 320 | 321 | # Write out the makefile. 322 | # First, delete the Makefile if it exists. 323 | deleteFileIfExists("Makefile") 324 | # Now write out the Makefile. 325 | with open("Makefile", "w") as f: 326 | f.write(makefile) 327 | 328 | if args.make is True: 329 | subprocess.check_call("make", shell=True) 330 | clean() 331 | 332 | print "Done." 333 | 334 | if __name__ == "__main__": 335 | main() -------------------------------------------------------------------------------- /dep/linenoise-ng/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | DerivePointerAlignment: false 3 | PointerAlignment: Left 4 | Standard: Cpp11 5 | -------------------------------------------------------------------------------- /dep/linenoise-ng/.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | build 5 | cmake_install.cmake 6 | install_manifest.txt 7 | example 8 | linenoise_example 9 | liblinenoise.a 10 | *.dSYM 11 | history.txt 12 | *.o 13 | *~ 14 | -------------------------------------------------------------------------------- /dep/linenoise-ng/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- mode: CMAKE; -*- 2 | 3 | cmake_minimum_required(VERSION 2.6) 4 | 5 | project(linenoise) 6 | 7 | set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/build") 8 | 9 | if(NOT CMAKE_BUILD_TYPE) 10 | set(CMAKE_BUILD_TYPE Release CACHE string "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) 11 | endif() 12 | 13 | message(STATUS "Build mode: ${CMAKE_BUILD_TYPE}") 14 | 15 | # INFO 16 | set(LINENOISE_VERSION "1.0.0" CACHE path "Linenoise version") 17 | set(LINENOISE_DISPLAY_NAME "Linenoise-NG") 18 | set(LINENOISE_URL_INFO_ABOUT "https://github.com/arangodb/linenoise-ng") 19 | set(LINENOISE_CONTACT "hackers@arangodb.org") 20 | set(LINENOISE_FRIENDLY_STRING "Linenoise NG - Linenoise Next Generation") 21 | 22 | # compiler options 23 | if(CMAKE_COMPILER_IS_GNUCXX) 24 | message(STATUS "Compiler type GNU: ${CMAKE_CXX_COMPILER}") 25 | set(BASE_COMPILER_OPTIONS "-std=c++11 -Wall -Wextra") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}") 27 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g") 28 | set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${BASE_COMPILER_OPTIONS} -Os") 29 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer") 30 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${BASE_COMPILER_OPTIONS} -O3 -g") 31 | 32 | elseif(CMAKE_COMPILER_IS_CLANGCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 33 | # using regular Clang or AppleClang 34 | message(STATUS "Compiler type CLANG: ${CMAKE_CXX_COMPILER}") 35 | set(BASE_COMPILER_OPTIONS "-std=c++11 -Wall -Wextra") 36 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}") 37 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g") 38 | set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${BASE_COMPILER_OPTIONS} -Os") 39 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer") 40 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${BASE_COMPILER_OPTIONS} -O3 -g") 41 | 42 | elseif(MSVC) 43 | message(STATUS "Compiler type MSVC: ${CMAKE_CXX_COMPILER}") 44 | add_definitions("-D_CRT_SECURE_NO_WARNINGS=1") 45 | 46 | foreach (flag_var 47 | CMAKE_CXX_FLAGS 48 | CMAKE_CXX_FLAGS_DEBUG 49 | CMAKE_CXX_FLAGS_RELEASE 50 | CMAKE_CXX_FLAGS_MINSIZEREL 51 | CMAKE_CXX_FLAGS_RELWITHDEBINFO) 52 | if (flag_var MATCHES "DEBUG") 53 | set(${flag_var} "${${flag_var}} /MTd") 54 | else () 55 | set(${flag_var} "${${flag_var}} /MT") 56 | endif () 57 | endforeach() 58 | # https://msdn.microsoft.com/en-us/library/aa267384%28VS.60%29.aspx 59 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO /SUBSYSTEM:CONSOLE /LTCG /ignore:4099 /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:msvcrtd.lib") 60 | set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /SUBSYSTEM:CONSOLE /ignore:4099 /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:msvcrtd.lib") 61 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SUBSYSTEM:CONSOLE /ignore:4099 /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:msvcrtd.lib") 62 | set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /SUBSYSTEM:CONSOLE /ignore:4099 /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:msvcrtd.lib") 63 | else() 64 | # unknown compiler 65 | message(STATUS "Compiler type UNKNOWN: ${CMAKE_CXX_COMPILER}") 66 | set(BASE_COMPILER_OPTIONS "-std=c++11 -Wall -Wextra") 67 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}") 68 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g") 69 | set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${BASE_COMPILER_OPTIONS} -Os") 70 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer") 71 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${BASE_COMPILER_OPTIONS} -O3 -g") 72 | 73 | endif() 74 | 75 | include_directories(${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src) 76 | 77 | # build liblinenoise 78 | add_library( 79 | linenoise 80 | STATIC 81 | src/ConvertUTF.cpp 82 | src/linenoise.cpp 83 | src/wcwidth.cpp 84 | ) 85 | 86 | # install 87 | install(TARGETS linenoise DESTINATION lib) 88 | 89 | # headers 90 | install(FILES include/linenoise.h DESTINATION include) 91 | 92 | # build example 93 | add_executable( 94 | example 95 | tst/example.c 96 | ) 97 | 98 | target_link_libraries( 99 | example 100 | linenoise 101 | ) 102 | 103 | # packaging 104 | include(CPack) 105 | 106 | if (MSVC) 107 | else () 108 | set(CPACK_SET_DESTDIR ON) 109 | endif () 110 | 111 | set(CPACK_PACKAGE_VENDOR "ArangoDB GmbH") 112 | set(CPACK_PACKAGE_CONTACT "info@arangodb.com") 113 | set(CPACK_PACKAGE_VERSION "${LINENOISE_VERSION}") 114 | 115 | set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") 116 | 117 | set(CPACK_STRIP_FILES "ON") 118 | 119 | set(CPACK_PACKAGE_NAME "linenoise") 120 | set(CPACK_DEBIAN_PACKAGE_SECTION "utilities") 121 | -------------------------------------------------------------------------------- /dep/linenoise-ng/LICENSE: -------------------------------------------------------------------------------- 1 | linenoise.cpp 2 | ============= 3 | 4 | Copyright (c) 2010, Salvatore Sanfilippo 5 | Copyright (c) 2010, Pieter Noordhuis 6 | 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | * Neither the name of Redis nor the names of its contributors may be used 18 | to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | 33 | 34 | wcwidth.cpp 35 | =========== 36 | 37 | Markus Kuhn -- 2007-05-26 (Unicode 5.0) 38 | 39 | Permission to use, copy, modify, and distribute this software 40 | for any purpose and without fee is hereby granted. The author 41 | disclaims all warranties with regard to this software. 42 | 43 | 44 | 45 | ConvertUTF.cpp 46 | ============== 47 | 48 | Copyright 2001-2004 Unicode, Inc. 49 | 50 | Disclaimer 51 | 52 | This source code is provided as is by Unicode, Inc. No claims are 53 | made as to fitness for any particular purpose. No warranties of any 54 | kind are expressed or implied. The recipient agrees to determine 55 | applicability of information provided. If this file has been 56 | purchased on magnetic or optical media from Unicode, Inc., the 57 | sole remedy for any claim will be exchange of defective media 58 | within 90 days of receipt. 59 | 60 | Limitations on Rights to Redistribute This Code 61 | 62 | Unicode, Inc. hereby grants the right to freely use the information 63 | supplied in this file in the creation of products supporting the 64 | Unicode Standard, and to make copies of this file in any form 65 | for internal or external distribution as long as this notice 66 | remains attached. 67 | -------------------------------------------------------------------------------- /dep/linenoise-ng/README.md: -------------------------------------------------------------------------------- 1 | # Linenoise Next Generation 2 | 3 | A small, portable GNU readline replacement for Linux, Windows and 4 | MacOS which is capable of handling UTF-8 characters. Unlike GNU 5 | readline, which is GPL, this library uses a BSD license and can be 6 | used in any kind of program. 7 | 8 | ## Origin 9 | 10 | This linenoise implementation is based on the work by 11 | [Salvatore Sanfilippo](https://github.com/antirez/linenoise) and 12 | 10gen Inc. The goal is to create a zero-config, BSD 13 | licensed, readline replacement usable in Apache2 or BSD licensed 14 | programs. 15 | 16 | ## Features 17 | 18 | * single-line and multi-line editing mode with the usual key bindings implemented 19 | * history handling 20 | * completion 21 | * BSD license source code 22 | * Only uses a subset of VT100 escapes (ANSI.SYS compatible) 23 | * UTF8 aware 24 | * support for Linux, MacOS and Windows 25 | 26 | It deviates from Salvatore's original goal to have a minimal readline 27 | replacement for the sake of supporting UTF8 and Windows. It deviates 28 | from 10gen Inc.'s goal to create a C++ interface to linenoise. This 29 | library uses C++ internally, but to the user it provides a pure C 30 | interface that is compatible with the original linenoise API. 31 | C interface. 32 | 33 | ## Requirements 34 | 35 | To build this library, you will need a C++11-enabled compiler and 36 | some recent version of CMake. 37 | 38 | ## Build instructions 39 | 40 | To build this library on Linux, first create a build directory 41 | 42 | ```bash 43 | mkdir -p build 44 | ``` 45 | 46 | and then build the library: 47 | 48 | ```bash 49 | (cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make) 50 | ``` 51 | 52 | To build and install the library at the default target location, use 53 | 54 | ```bash 55 | (cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make && sudo make install) 56 | ``` 57 | 58 | The default installation location can be adjusted by setting the `DESTDIR` 59 | variable when invoking `make install`: 60 | 61 | ```bash 62 | (cd build && make DESTDIR=/tmp install) 63 | ``` 64 | 65 | To build the library on Windows, use these commands in an MS-DOS command 66 | prompt: 67 | 68 | ``` 69 | md build 70 | cd build 71 | ``` 72 | 73 | After that, invoke the appropriate command to create the files for your 74 | target environment: 75 | 76 | * 32 bit: `cmake -G "Visual Studio 12 2013" -DCMAKE_BUILD_TYPE=Release ..` 77 | * 64 bit: `cmake -G "Visual Studio 12 2013 Win64" -DCMAKE_BUILD_TYPE=Release ..` 78 | 79 | After that, open the generated file `linenoise.sln` from the `build` 80 | subdirectory with Visual Studio. 81 | 82 | 83 | *note: the following sections of the README.md are from the original 84 | linenoise repository and are partly outdated* 85 | 86 | ## Can a line editing library be 20k lines of code? 87 | 88 | Line editing with some support for history is a really important 89 | feature for command line utilities. Instead of retyping almost the 90 | same stuff again and again it's just much better to hit the up arrow 91 | and edit on syntax errors, or in order to try a slightly different 92 | command. But apparently code dealing with terminals is some sort of 93 | Black Magic: readline is 30k lines of code, libedit 20k. Is it 94 | reasonable to link small utilities to huge libraries just to get a 95 | minimal support for line editing? 96 | 97 | So what usually happens is either: 98 | 99 | * Large programs with configure scripts disabling line editing if 100 | readline is not present in the system, or not supporting it at all 101 | since readline is GPL licensed and libedit (the BSD clone) is not 102 | as known and available as readline is (Real world example of this 103 | problem: Tclsh). 104 | 105 | * Smaller programs not using a configure script not supporting line 106 | editing at all (A problem we had with Redis-cli for instance). 107 | 108 | The result is a pollution of binaries without line editing support. 109 | 110 | So Salvatore spent more or less two hours doing a reality check 111 | resulting in this little library: is it *really* needed for a line 112 | editing library to be 20k lines of code? Apparently not, it is possibe 113 | to get a very small, zero configuration, trivial to embed library, 114 | that solves the problem. Smaller programs will just include this, 115 | supporing line editing out of the box. Larger programs may use this 116 | little library or just checking with configure if readline/libedit is 117 | available and resorting to linenoise if not. 118 | 119 | ## Terminals, in 2010. 120 | 121 | Apparently almost every terminal you can happen to use today has some 122 | kind of support for basic VT100 escape sequences. So Salvatore tried 123 | to write a lib using just very basic VT100 features. The resulting 124 | library appears to work everywhere Salvatore tried to use it, and now 125 | can work even on ANSI.SYS compatible terminals, since no VT220 126 | specific sequences are used anymore. 127 | 128 | The original library has currently about 1100 lines of code. In order 129 | to use it in your project just look at the *example.c* file in the 130 | source distribution, it is trivial. Linenoise is BSD code, so you can 131 | use both in free software and commercial software. 132 | 133 | ## Tested with... 134 | 135 | * Linux text only console ($TERM = linux) 136 | * Linux KDE terminal application ($TERM = xterm) 137 | * Linux xterm ($TERM = xterm) 138 | * Linux Buildroot ($TERM = vt100) 139 | * Mac OS X iTerm ($TERM = xterm) 140 | * Mac OS X default Terminal.app ($TERM = xterm) 141 | * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) 142 | * IBM AIX 6.1 143 | * FreeBSD xterm ($TERM = xterm) 144 | * ANSI.SYS 145 | * Emacs comint mode ($TERM = dumb) 146 | * Windows 147 | 148 | Please test it everywhere you can and report back! 149 | 150 | ## Let's push this forward! 151 | 152 | Patches should be provided in the respect of linenoise sensibility for 153 | small and easy to understand code that and the license 154 | restrictions. Extensions must be submitted under a BSD license-style. 155 | A contributor license is required for contributions. 156 | 157 | -------------------------------------------------------------------------------- /dep/linenoise-ng/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | branches: 3 | only: 4 | - master 5 | configuration: Release 6 | build: 7 | build_script: 8 | - md build 9 | - cd %APPVEYOR_BUILD_FOLDER%\build 10 | - cmake -G "Visual Studio 12 Win64" -DCMAKE_BUILD_TYPE=Release .. 11 | - cmake --build . --config Release 12 | -------------------------------------------------------------------------------- /dep/linenoise-ng/include/linenoise.h: -------------------------------------------------------------------------------- 1 | /* linenoise.h -- guerrilla line editing library against the idea that a 2 | * line editing lib needs to be 20,000 lines of C code. 3 | * 4 | * See linenoise.c for more information. 5 | * 6 | * Copyright (c) 2010, Salvatore Sanfilippo 7 | * Copyright (c) 2010, Pieter Noordhuis 8 | * 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * * Neither the name of Redis nor the names of its contributors may be used 20 | * to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #ifndef __LINENOISE_H 37 | #define __LINENOISE_H 38 | 39 | #define LINENOISE_VERSION "1.0.0" 40 | #define LINENOISE_VERSION_MAJOR 1 41 | #define LINENOISE_VERSION_MINOR 1 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | typedef struct linenoiseCompletions linenoiseCompletions; 48 | 49 | typedef void(linenoiseCompletionCallback)(const char*, linenoiseCompletions*); 50 | void linenoiseSetCompletionCallback(linenoiseCompletionCallback* fn); 51 | void linenoiseAddCompletion(linenoiseCompletions* lc, const char* str); 52 | 53 | char* linenoise(const char* prompt); 54 | void linenoisePreloadBuffer(const char* preloadText); 55 | int linenoiseHistoryAdd(const char* line); 56 | int linenoiseHistorySetMaxLen(int len); 57 | char* linenoiseHistoryLine(int index); 58 | int linenoiseHistorySave(const char* filename); 59 | int linenoiseHistoryLoad(const char* filename); 60 | void linenoiseHistoryFree(void); 61 | void linenoiseClearScreen(void); 62 | void linenoiseSetMultiLine(int ml); 63 | void linenoisePrintKeyCodes(void); 64 | /* the following are extensions to the original linenoise API */ 65 | int linenoiseInstallWindowChangeHandler(void); 66 | /* returns type of key pressed: 1 = CTRL-C, 2 = CTRL-D, 0 = other */ 67 | int linenoiseKeyType(void); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif /* __LINENOISE_H */ 74 | -------------------------------------------------------------------------------- /dep/linenoise-ng/src/ConvertUTF.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2001-2004 Unicode, Inc. 3 | * 4 | * Disclaimer 5 | * 6 | * This source code is provided as is by Unicode, Inc. No claims are 7 | * made as to fitness for any particular purpose. No warranties of any 8 | * kind are expressed or implied. The recipient agrees to determine 9 | * applicability of information provided. If this file has been 10 | * purchased on magnetic or optical media from Unicode, Inc., the 11 | * sole remedy for any claim will be exchange of defective media 12 | * within 90 days of receipt. 13 | * 14 | * Limitations on Rights to Redistribute This Code 15 | * 16 | * Unicode, Inc. hereby grants the right to freely use the information 17 | * supplied in this file in the creation of products supporting the 18 | * Unicode Standard, and to make copies of this file in any form 19 | * for internal or external distribution as long as this notice 20 | * remains attached. 21 | */ 22 | 23 | /* --------------------------------------------------------------------- 24 | 25 | Conversions between UTF32, UTF-16, and UTF-8. Source code file. 26 | Author: Mark E. Davis, 1994. 27 | Rev History: Rick McGowan, fixes & updates May 2001. 28 | Sept 2001: fixed const & error conditions per 29 | mods suggested by S. Parent & A. Lillich. 30 | June 2002: Tim Dodd added detection and handling of incomplete 31 | source sequences, enhanced error detection, added casts 32 | to eliminate compiler warnings. 33 | July 2003: slight mods to back out aggressive FFFE detection. 34 | Jan 2004: updated switches in from-UTF8 conversions. 35 | Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. 36 | 37 | See the header file "ConvertUTF.h" for complete documentation. 38 | 39 | ------------------------------------------------------------------------ */ 40 | 41 | #include "ConvertUTF.h" 42 | #ifdef CVTUTF_DEBUG 43 | #include 44 | #endif 45 | 46 | namespace linenoise_ng { 47 | 48 | static const int halfShift = 10; /* used for shifting by 10 bits */ 49 | 50 | static const UTF32 halfBase = 0x0010000UL; 51 | static const UTF32 halfMask = 0x3FFUL; 52 | 53 | #define UNI_SUR_HIGH_START (UTF32)0xD800 54 | #define UNI_SUR_HIGH_END (UTF32)0xDBFF 55 | #define UNI_SUR_LOW_START (UTF32)0xDC00 56 | #define UNI_SUR_LOW_END (UTF32)0xDFFF 57 | #define false 0 58 | #define true 1 59 | 60 | /* --------------------------------------------------------------------- */ 61 | 62 | ConversionResult ConvertUTF32toUTF16 ( 63 | const UTF32** sourceStart, const UTF32* sourceEnd, 64 | char16_t** targetStart, char16_t* targetEnd, ConversionFlags flags) { 65 | ConversionResult result = conversionOK; 66 | const UTF32* source = *sourceStart; 67 | char16_t* target = *targetStart; 68 | while (source < sourceEnd) { 69 | UTF32 ch; 70 | if (target >= targetEnd) { 71 | result = targetExhausted; break; 72 | } 73 | ch = *source++; 74 | if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ 75 | /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ 76 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 77 | if (flags == strictConversion) { 78 | --source; /* return to the illegal value itself */ 79 | result = sourceIllegal; 80 | break; 81 | } else { 82 | *target++ = UNI_REPLACEMENT_CHAR; 83 | } 84 | } else { 85 | *target++ = (UTF16)ch; /* normal case */ 86 | } 87 | } else if (ch > UNI_MAX_LEGAL_UTF32) { 88 | if (flags == strictConversion) { 89 | result = sourceIllegal; 90 | } else { 91 | *target++ = UNI_REPLACEMENT_CHAR; 92 | } 93 | } else { 94 | /* target is a character in range 0xFFFF - 0x10FFFF. */ 95 | if (target + 1 >= targetEnd) { 96 | --source; /* Back up source pointer! */ 97 | result = targetExhausted; break; 98 | } 99 | ch -= halfBase; 100 | *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); 101 | *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); 102 | } 103 | } 104 | *sourceStart = source; 105 | *targetStart = target; 106 | return result; 107 | } 108 | 109 | /* --------------------------------------------------------------------- */ 110 | 111 | ConversionResult ConvertUTF16toUTF32 ( 112 | const UTF16** sourceStart, const UTF16* sourceEnd, 113 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { 114 | ConversionResult result = conversionOK; 115 | const UTF16* source = *sourceStart; 116 | UTF32* target = *targetStart; 117 | UTF32 ch, ch2; 118 | while (source < sourceEnd) { 119 | const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ 120 | ch = *source++; 121 | /* If we have a surrogate pair, convert to UTF32 first. */ 122 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { 123 | /* If the 16 bits following the high surrogate are in the source buffer... */ 124 | if (source < sourceEnd) { 125 | ch2 = *source; 126 | /* If it's a low surrogate, convert to UTF32. */ 127 | if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { 128 | ch = ((ch - UNI_SUR_HIGH_START) << halfShift) 129 | + (ch2 - UNI_SUR_LOW_START) + halfBase; 130 | ++source; 131 | } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ 132 | --source; /* return to the illegal value itself */ 133 | result = sourceIllegal; 134 | break; 135 | } 136 | } else { /* We don't have the 16 bits following the high surrogate. */ 137 | --source; /* return to the high surrogate */ 138 | result = sourceExhausted; 139 | break; 140 | } 141 | } else if (flags == strictConversion) { 142 | /* UTF-16 surrogate values are illegal in UTF-32 */ 143 | if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { 144 | --source; /* return to the illegal value itself */ 145 | result = sourceIllegal; 146 | break; 147 | } 148 | } 149 | if (target >= targetEnd) { 150 | source = oldSource; /* Back up source pointer! */ 151 | result = targetExhausted; break; 152 | } 153 | *target++ = ch; 154 | } 155 | *sourceStart = source; 156 | *targetStart = target; 157 | #ifdef CVTUTF_DEBUG 158 | if (result == sourceIllegal) { 159 | fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); 160 | fflush(stderr); 161 | } 162 | #endif 163 | return result; 164 | } 165 | 166 | /* --------------------------------------------------------------------- */ 167 | 168 | /* 169 | * Index into the table below with the first byte of a UTF-8 sequence to 170 | * get the number of trailing bytes that are supposed to follow it. 171 | * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is 172 | * left as-is for anyone who may want to do such conversion, which was 173 | * allowed in earlier algorithms. 174 | */ 175 | static const char trailingBytesForUTF8[256] = { 176 | 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, 177 | 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, 178 | 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, 179 | 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, 180 | 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, 181 | 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, 182 | 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, 183 | 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 184 | }; 185 | 186 | /* 187 | * Magic values subtracted from a buffer value during UTF8 conversion. 188 | * This table contains as many values as there might be trailing bytes 189 | * in a UTF-8 sequence. 190 | */ 191 | static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 192 | 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; 193 | 194 | /* 195 | * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed 196 | * into the first byte, depending on how many bytes follow. There are 197 | * as many entries in this table as there are UTF-8 sequence types. 198 | * (I.e., one byte sequence, two byte... etc.). Remember that sequencs 199 | * for *legal* UTF-8 will be 4 or fewer bytes total. 200 | */ 201 | static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 202 | 203 | /* --------------------------------------------------------------------- */ 204 | 205 | /* The interface converts a whole buffer to avoid function-call overhead. 206 | * Constants have been gathered. Loops & conditionals have been removed as 207 | * much as possible for efficiency, in favor of drop-through switches. 208 | * (See "Note A" at the bottom of the file for equivalent code.) 209 | * If your compiler supports it, the "isLegalUTF8" call can be turned 210 | * into an inline function. 211 | */ 212 | 213 | /* --------------------------------------------------------------------- */ 214 | 215 | ConversionResult ConvertUTF16toUTF8 ( 216 | const UTF16** sourceStart, const UTF16* sourceEnd, 217 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { 218 | ConversionResult result = conversionOK; 219 | const UTF16* source = *sourceStart; 220 | UTF8* target = *targetStart; 221 | while (source < sourceEnd) { 222 | UTF32 ch; 223 | unsigned short bytesToWrite = 0; 224 | const UTF32 byteMask = 0xBF; 225 | const UTF32 byteMark = 0x80; 226 | const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ 227 | ch = *source++; 228 | /* If we have a surrogate pair, convert to UTF32 first. */ 229 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { 230 | /* If the 16 bits following the high surrogate are in the source buffer... */ 231 | if (source < sourceEnd) { 232 | UTF32 ch2 = *source; 233 | /* If it's a low surrogate, convert to UTF32. */ 234 | if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { 235 | ch = ((ch - UNI_SUR_HIGH_START) << halfShift) 236 | + (ch2 - UNI_SUR_LOW_START) + halfBase; 237 | ++source; 238 | } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ 239 | --source; /* return to the illegal value itself */ 240 | result = sourceIllegal; 241 | break; 242 | } 243 | } else { /* We don't have the 16 bits following the high surrogate. */ 244 | --source; /* return to the high surrogate */ 245 | result = sourceExhausted; 246 | break; 247 | } 248 | } else if (flags == strictConversion) { 249 | /* UTF-16 surrogate values are illegal in UTF-32 */ 250 | if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { 251 | --source; /* return to the illegal value itself */ 252 | result = sourceIllegal; 253 | break; 254 | } 255 | } 256 | /* Figure out how many bytes the result will require */ 257 | if (ch < (UTF32)0x80) { bytesToWrite = 1; 258 | } else if (ch < (UTF32)0x800) { bytesToWrite = 2; 259 | } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; 260 | } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; 261 | } else { bytesToWrite = 3; 262 | ch = UNI_REPLACEMENT_CHAR; 263 | } 264 | 265 | target += bytesToWrite; 266 | if (target > targetEnd) { 267 | source = oldSource; /* Back up source pointer! */ 268 | target -= bytesToWrite; result = targetExhausted; break; 269 | } 270 | switch (bytesToWrite) { /* note: everything falls through. */ 271 | case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 272 | case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 273 | case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 274 | case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); 275 | } 276 | target += bytesToWrite; 277 | } 278 | *sourceStart = source; 279 | *targetStart = target; 280 | return result; 281 | } 282 | 283 | /* --------------------------------------------------------------------- */ 284 | 285 | /* 286 | * Utility routine to tell whether a sequence of bytes is legal UTF-8. 287 | * This must be called with the length pre-determined by the first byte. 288 | * If not calling this from ConvertUTF8to*, then the length can be set by: 289 | * length = trailingBytesForUTF8[*source]+1; 290 | * and the sequence is illegal right away if there aren't that many bytes 291 | * available. 292 | * If presented with a length > 4, this returns false. The Unicode 293 | * definition of UTF-8 goes up to 4-byte sequences. 294 | */ 295 | 296 | static Boolean isLegalUTF8(const UTF8 *source, int length) { 297 | UTF8 a; 298 | const UTF8 *srcptr = source+length; 299 | switch (length) { 300 | default: return false; 301 | /* Everything else falls through when "true"... */ 302 | case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; 303 | case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; 304 | case 2: if ((a = (*--srcptr)) > 0xBF) return false; 305 | 306 | switch (*source) { 307 | /* no fall-through in this inner switch */ 308 | case 0xE0: if (a < 0xA0) return false; break; 309 | case 0xED: if (a > 0x9F) return false; break; 310 | case 0xF0: if (a < 0x90) return false; break; 311 | case 0xF4: if (a > 0x8F) return false; break; 312 | default: if (a < 0x80) return false; 313 | } 314 | 315 | case 1: if (*source >= 0x80 && *source < 0xC2) return false; 316 | } 317 | if (*source > 0xF4) return false; 318 | return true; 319 | } 320 | 321 | /* --------------------------------------------------------------------- */ 322 | 323 | /* 324 | * Exported function to return whether a UTF-8 sequence is legal or not. 325 | * This is not used here; it's just exported. 326 | */ 327 | Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { 328 | int length = trailingBytesForUTF8[*source]+1; 329 | if (source+length > sourceEnd) { 330 | return false; 331 | } 332 | return isLegalUTF8(source, length); 333 | } 334 | 335 | /* --------------------------------------------------------------------- */ 336 | 337 | ConversionResult ConvertUTF8toUTF16 ( 338 | const UTF8** sourceStart, const UTF8* sourceEnd, 339 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { 340 | ConversionResult result = conversionOK; 341 | const UTF8* source = *sourceStart; 342 | UTF16* target = *targetStart; 343 | while (source < sourceEnd) { 344 | UTF32 ch = 0; 345 | unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; 346 | if (source + extraBytesToRead >= sourceEnd) { 347 | result = sourceExhausted; break; 348 | } 349 | /* Do this check whether lenient or strict */ 350 | if (! isLegalUTF8(source, extraBytesToRead+1)) { 351 | result = sourceIllegal; 352 | break; 353 | } 354 | /* 355 | * The cases all fall through. See "Note A" below. 356 | */ 357 | switch (extraBytesToRead) { 358 | case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ 359 | case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ 360 | case 3: ch += *source++; ch <<= 6; 361 | case 2: ch += *source++; ch <<= 6; 362 | case 1: ch += *source++; ch <<= 6; 363 | case 0: ch += *source++; 364 | } 365 | ch -= offsetsFromUTF8[extraBytesToRead]; 366 | 367 | if (target >= targetEnd) { 368 | source -= (extraBytesToRead+1); /* Back up source pointer! */ 369 | result = targetExhausted; break; 370 | } 371 | if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ 372 | /* UTF-16 surrogate values are illegal in UTF-32 */ 373 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 374 | if (flags == strictConversion) { 375 | source -= (extraBytesToRead+1); /* return to the illegal value itself */ 376 | result = sourceIllegal; 377 | break; 378 | } else { 379 | *target++ = UNI_REPLACEMENT_CHAR; 380 | } 381 | } else { 382 | *target++ = (UTF16)ch; /* normal case */ 383 | } 384 | } else if (ch > UNI_MAX_UTF16) { 385 | if (flags == strictConversion) { 386 | result = sourceIllegal; 387 | source -= (extraBytesToRead+1); /* return to the start */ 388 | break; /* Bail out; shouldn't continue */ 389 | } else { 390 | *target++ = UNI_REPLACEMENT_CHAR; 391 | } 392 | } else { 393 | /* target is a character in range 0xFFFF - 0x10FFFF. */ 394 | if (target + 1 >= targetEnd) { 395 | source -= (extraBytesToRead+1); /* Back up source pointer! */ 396 | result = targetExhausted; break; 397 | } 398 | ch -= halfBase; 399 | *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); 400 | *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); 401 | } 402 | } 403 | *sourceStart = source; 404 | *targetStart = target; 405 | return result; 406 | } 407 | 408 | /* --------------------------------------------------------------------- */ 409 | 410 | ConversionResult ConvertUTF32toUTF8 ( 411 | const UTF32** sourceStart, const UTF32* sourceEnd, 412 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { 413 | ConversionResult result = conversionOK; 414 | const UTF32* source = *sourceStart; 415 | UTF8* target = *targetStart; 416 | while (source < sourceEnd) { 417 | UTF32 ch; 418 | unsigned short bytesToWrite = 0; 419 | const UTF32 byteMask = 0xBF; 420 | const UTF32 byteMark = 0x80; 421 | ch = *source++; 422 | if (flags == strictConversion ) { 423 | /* UTF-16 surrogate values are illegal in UTF-32 */ 424 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 425 | --source; /* return to the illegal value itself */ 426 | result = sourceIllegal; 427 | break; 428 | } 429 | } 430 | /* 431 | * Figure out how many bytes the result will require. Turn any 432 | * illegally large UTF32 things (> Plane 17) into replacement chars. 433 | */ 434 | if (ch < (UTF32)0x80) { bytesToWrite = 1; 435 | } else if (ch < (UTF32)0x800) { bytesToWrite = 2; 436 | } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; 437 | } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; 438 | } else { bytesToWrite = 3; 439 | ch = UNI_REPLACEMENT_CHAR; 440 | result = sourceIllegal; 441 | } 442 | 443 | target += bytesToWrite; 444 | if (target > targetEnd) { 445 | --source; /* Back up source pointer! */ 446 | target -= bytesToWrite; result = targetExhausted; break; 447 | } 448 | switch (bytesToWrite) { /* note: everything falls through. */ 449 | case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 450 | case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 451 | case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 452 | case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); 453 | } 454 | target += bytesToWrite; 455 | } 456 | *sourceStart = source; 457 | *targetStart = target; 458 | return result; 459 | } 460 | 461 | /* --------------------------------------------------------------------- */ 462 | 463 | ConversionResult ConvertUTF8toUTF32 ( 464 | const UTF8** sourceStart, const UTF8* sourceEnd, 465 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { 466 | ConversionResult result = conversionOK; 467 | const UTF8* source = *sourceStart; 468 | UTF32* target = *targetStart; 469 | while (source < sourceEnd) { 470 | UTF32 ch = 0; 471 | unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; 472 | if (source + extraBytesToRead >= sourceEnd) { 473 | result = sourceExhausted; break; 474 | } 475 | /* Do this check whether lenient or strict */ 476 | if (! isLegalUTF8(source, extraBytesToRead+1)) { 477 | result = sourceIllegal; 478 | break; 479 | } 480 | /* 481 | * The cases all fall through. See "Note A" below. 482 | */ 483 | switch (extraBytesToRead) { 484 | case 5: ch += *source++; ch <<= 6; 485 | case 4: ch += *source++; ch <<= 6; 486 | case 3: ch += *source++; ch <<= 6; 487 | case 2: ch += *source++; ch <<= 6; 488 | case 1: ch += *source++; ch <<= 6; 489 | case 0: ch += *source++; 490 | } 491 | ch -= offsetsFromUTF8[extraBytesToRead]; 492 | 493 | if (target >= targetEnd) { 494 | source -= (extraBytesToRead+1); /* Back up the source pointer! */ 495 | result = targetExhausted; break; 496 | } 497 | if (ch <= UNI_MAX_LEGAL_UTF32) { 498 | /* 499 | * UTF-16 surrogate values are illegal in UTF-32, and anything 500 | * over Plane 17 (> 0x10FFFF) is illegal. 501 | */ 502 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 503 | if (flags == strictConversion) { 504 | source -= (extraBytesToRead+1); /* return to the illegal value itself */ 505 | result = sourceIllegal; 506 | break; 507 | } else { 508 | *target++ = UNI_REPLACEMENT_CHAR; 509 | } 510 | } else { 511 | *target++ = ch; 512 | } 513 | } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ 514 | result = sourceIllegal; 515 | *target++ = UNI_REPLACEMENT_CHAR; 516 | } 517 | } 518 | *sourceStart = source; 519 | *targetStart = target; 520 | return result; 521 | } 522 | 523 | } 524 | 525 | /* --------------------------------------------------------------------- 526 | 527 | Note A. 528 | The fall-through switches in UTF-8 reading code save a 529 | temp variable, some decrements & conditionals. The switches 530 | are equivalent to the following loop: 531 | { 532 | int tmpBytesToRead = extraBytesToRead+1; 533 | do { 534 | ch += *source++; 535 | --tmpBytesToRead; 536 | if (tmpBytesToRead) ch <<= 6; 537 | } while (tmpBytesToRead > 0); 538 | } 539 | In UTF-8 writing code, the switches on "bytesToWrite" are 540 | similarly unrolled loops. 541 | 542 | --------------------------------------------------------------------- */ 543 | -------------------------------------------------------------------------------- /dep/linenoise-ng/src/ConvertUTF.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2001-2004 Unicode, Inc. 3 | * 4 | * Disclaimer 5 | * 6 | * This source code is provided as is by Unicode, Inc. No claims are 7 | * made as to fitness for any particular purpose. No warranties of any 8 | * kind are expressed or implied. The recipient agrees to determine 9 | * applicability of information provided. If this file has been 10 | * purchased on magnetic or optical media from Unicode, Inc., the 11 | * sole remedy for any claim will be exchange of defective media 12 | * within 90 days of receipt. 13 | * 14 | * Limitations on Rights to Redistribute This Code 15 | * 16 | * Unicode, Inc. hereby grants the right to freely use the information 17 | * supplied in this file in the creation of products supporting the 18 | * Unicode Standard, and to make copies of this file in any form 19 | * for internal or external distribution as long as this notice 20 | * remains attached. 21 | */ 22 | 23 | /* --------------------------------------------------------------------- 24 | 25 | Conversions between UTF32, UTF-16, and UTF-8. Header file. 26 | 27 | Several funtions are included here, forming a complete set of 28 | conversions between the three formats. UTF-7 is not included 29 | here, but is handled in a separate source file. 30 | 31 | Each of these routines takes pointers to input buffers and output 32 | buffers. The input buffers are const. 33 | 34 | Each routine converts the text between *sourceStart and sourceEnd, 35 | putting the result into the buffer between *targetStart and 36 | targetEnd. Note: the end pointers are *after* the last item: e.g. 37 | *(sourceEnd - 1) is the last item. 38 | 39 | The return result indicates whether the conversion was successful, 40 | and if not, whether the problem was in the source or target buffers. 41 | (Only the first encountered problem is indicated.) 42 | 43 | After the conversion, *sourceStart and *targetStart are both 44 | updated to point to the end of last text successfully converted in 45 | the respective buffers. 46 | 47 | Input parameters: 48 | sourceStart - pointer to a pointer to the source buffer. 49 | The contents of this are modified on return so that 50 | it points at the next thing to be converted. 51 | targetStart - similarly, pointer to pointer to the target buffer. 52 | sourceEnd, targetEnd - respectively pointers to the ends of the 53 | two buffers, for overflow checking only. 54 | 55 | These conversion functions take a ConversionFlags argument. When this 56 | flag is set to strict, both irregular sequences and isolated surrogates 57 | will cause an error. When the flag is set to lenient, both irregular 58 | sequences and isolated surrogates are converted. 59 | 60 | Whether the flag is strict or lenient, all illegal sequences will cause 61 | an error return. This includes sequences such as: , , 62 | or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code 63 | must check for illegal sequences. 64 | 65 | When the flag is set to lenient, characters over 0x10FFFF are converted 66 | to the replacement character; otherwise (when the flag is set to strict) 67 | they constitute an error. 68 | 69 | Output parameters: 70 | The value "sourceIllegal" is returned from some routines if the input 71 | sequence is malformed. When "sourceIllegal" is returned, the source 72 | value will point to the illegal value that caused the problem. E.g., 73 | in UTF-8 when a sequence is malformed, it points to the start of the 74 | malformed sequence. 75 | 76 | Author: Mark E. Davis, 1994. 77 | Rev History: Rick McGowan, fixes & updates May 2001. 78 | Fixes & updates, Sept 2001. 79 | 80 | ------------------------------------------------------------------------ */ 81 | 82 | /* --------------------------------------------------------------------- 83 | The following 4 definitions are compiler-specific. 84 | The C standard does not guarantee that wchar_t has at least 85 | 16 bits, so wchar_t is no less portable than unsigned short! 86 | All should be unsigned values to avoid sign extension during 87 | bit mask & shift operations. 88 | ------------------------------------------------------------------------ */ 89 | 90 | #if 0 91 | typedef unsigned long UTF32; /* at least 32 bits */ 92 | typedef unsigned short UTF16; /* at least 16 bits */ 93 | typedef unsigned char UTF8; /* typically 8 bits */ 94 | #endif 95 | 96 | #include 97 | #include 98 | 99 | namespace linenoise_ng { 100 | 101 | typedef uint32_t UTF32; 102 | typedef uint16_t UTF16; 103 | typedef uint8_t UTF8; 104 | typedef unsigned char Boolean; /* 0 or 1 */ 105 | 106 | /* Some fundamental constants */ 107 | #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD 108 | #define UNI_MAX_BMP (UTF32)0x0000FFFF 109 | #define UNI_MAX_UTF16 (UTF32)0x0010FFFF 110 | #define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF 111 | #define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF 112 | 113 | typedef enum { 114 | conversionOK, /* conversion successful */ 115 | sourceExhausted, /* partial character in source, but hit end */ 116 | targetExhausted, /* insuff. room in target for conversion */ 117 | sourceIllegal /* source sequence is illegal/malformed */ 118 | } ConversionResult; 119 | 120 | typedef enum { 121 | strictConversion = 0, 122 | lenientConversion 123 | } ConversionFlags; 124 | 125 | // /* This is for C++ and does no harm in C */ 126 | // #ifdef __cplusplus 127 | // extern "C" { 128 | // #endif 129 | 130 | ConversionResult ConvertUTF8toUTF16 ( 131 | const UTF8** sourceStart, const UTF8* sourceEnd, 132 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); 133 | 134 | ConversionResult ConvertUTF16toUTF8 ( 135 | const UTF16** sourceStart, const UTF16* sourceEnd, 136 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); 137 | 138 | ConversionResult ConvertUTF8toUTF32 ( 139 | const UTF8** sourceStart, const UTF8* sourceEnd, 140 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); 141 | 142 | ConversionResult ConvertUTF32toUTF8 ( 143 | const UTF32** sourceStart, const UTF32* sourceEnd, 144 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); 145 | 146 | ConversionResult ConvertUTF16toUTF32 ( 147 | const UTF16** sourceStart, const UTF16* sourceEnd, 148 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); 149 | 150 | ConversionResult ConvertUTF32toUTF16 ( 151 | const UTF32** sourceStart, const UTF32* sourceEnd, 152 | char16_t** targetStart, char16_t* targetEnd, ConversionFlags flags); 153 | 154 | Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); 155 | 156 | // #ifdef __cplusplus 157 | // } 158 | // #endif 159 | 160 | } 161 | 162 | /* --------------------------------------------------------------------- */ 163 | -------------------------------------------------------------------------------- /dep/linenoise-ng/src/wcwidth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an implementation of wcwidth() and wcswidth() (defined in 3 | * IEEE Std 1002.1-2001) for Unicode. 4 | * 5 | * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html 6 | * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html 7 | * 8 | * In fixed-width output devices, Latin characters all occupy a single 9 | * "cell" position of equal width, whereas ideographic CJK characters 10 | * occupy two such cells. Interoperability between terminal-line 11 | * applications and (teletype-style) character terminals using the 12 | * UTF-8 encoding requires agreement on which character should advance 13 | * the cursor by how many cell positions. No established formal 14 | * standards exist at present on which Unicode character shall occupy 15 | * how many cell positions on character terminals. These routines are 16 | * a first attempt of defining such behavior based on simple rules 17 | * applied to data provided by the Unicode Consortium. 18 | * 19 | * For some graphical characters, the Unicode standard explicitly 20 | * defines a character-cell width via the definition of the East Asian 21 | * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. 22 | * In all these cases, there is no ambiguity about which width a 23 | * terminal shall use. For characters in the East Asian Ambiguous (A) 24 | * class, the width choice depends purely on a preference of backward 25 | * compatibility with either historic CJK or Western practice. 26 | * Choosing single-width for these characters is easy to justify as 27 | * the appropriate long-term solution, as the CJK practice of 28 | * displaying these characters as double-width comes from historic 29 | * implementation simplicity (8-bit encoded characters were displayed 30 | * single-width and 16-bit ones double-width, even for Greek, 31 | * Cyrillic, etc.) and not any typographic considerations. 32 | * 33 | * Much less clear is the choice of width for the Not East Asian 34 | * (Neutral) class. Existing practice does not dictate a width for any 35 | * of these characters. It would nevertheless make sense 36 | * typographically to allocate two character cells to characters such 37 | * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be 38 | * represented adequately with a single-width glyph. The following 39 | * routines at present merely assign a single-cell width to all 40 | * neutral characters, in the interest of simplicity. This is not 41 | * entirely satisfactory and should be reconsidered before 42 | * establishing a formal standard in this area. At the moment, the 43 | * decision which Not East Asian (Neutral) characters should be 44 | * represented by double-width glyphs cannot yet be answered by 45 | * applying a simple rule from the Unicode database content. Setting 46 | * up a proper standard for the behavior of UTF-8 character terminals 47 | * will require a careful analysis not only of each Unicode character, 48 | * but also of each presentation form, something the author of these 49 | * routines has avoided to do so far. 50 | * 51 | * http://www.unicode.org/unicode/reports/tr11/ 52 | * 53 | * Markus Kuhn -- 2007-05-26 (Unicode 5.0) 54 | * 55 | * Permission to use, copy, modify, and distribute this software 56 | * for any purpose and without fee is hereby granted. The author 57 | * disclaims all warranties with regard to this software. 58 | * 59 | * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c 60 | */ 61 | 62 | #include 63 | #include 64 | #include 65 | 66 | namespace linenoise_ng { 67 | 68 | struct interval { 69 | char32_t first; 70 | char32_t last; 71 | }; 72 | 73 | /* auxiliary function for binary search in interval table */ 74 | static int bisearch(char32_t ucs, const struct interval *table, int max) { 75 | int min = 0; 76 | int mid; 77 | 78 | if (ucs < table[0].first || ucs > table[max].last) 79 | return 0; 80 | while (max >= min) { 81 | mid = (min + max) / 2; 82 | if (ucs > table[mid].last) 83 | min = mid + 1; 84 | else if (ucs < table[mid].first) 85 | max = mid - 1; 86 | else 87 | return 1; 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | 94 | /* The following two functions define the column width of an ISO 10646 95 | * character as follows: 96 | * 97 | * - The null character (U+0000) has a column width of 0. 98 | * 99 | * - Other C0/C1 control characters and DEL will lead to a return 100 | * value of -1. 101 | * 102 | * - Non-spacing and enclosing combining characters (general 103 | * category code Mn or Me in the Unicode database) have a 104 | * column width of 0. 105 | * 106 | * - SOFT HYPHEN (U+00AD) has a column width of 1. 107 | * 108 | * - Other format characters (general category code Cf in the Unicode 109 | * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. 110 | * 111 | * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) 112 | * have a column width of 0. 113 | * 114 | * - Spacing characters in the East Asian Wide (W) or East Asian 115 | * Full-width (F) category as defined in Unicode Technical 116 | * Report #11 have a column width of 2. 117 | * 118 | * - All remaining characters (including all printable 119 | * ISO 8859-1 and WGL4 characters, Unicode control characters, 120 | * etc.) have a column width of 1. 121 | * 122 | * This implementation assumes that wchar_t characters are encoded 123 | * in ISO 10646. 124 | */ 125 | 126 | int mk_wcwidth(char32_t ucs) 127 | { 128 | /* sorted list of non-overlapping intervals of non-spacing characters */ 129 | /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ 130 | static const struct interval combining[] = { 131 | { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, 132 | { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, 133 | { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, 134 | { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, 135 | { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, 136 | { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, 137 | { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, 138 | { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, 139 | { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, 140 | { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, 141 | { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, 142 | { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, 143 | { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, 144 | { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, 145 | { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, 146 | { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, 147 | { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, 148 | { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, 149 | { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, 150 | { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, 151 | { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, 152 | { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, 153 | { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, 154 | { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, 155 | { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, 156 | { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, 157 | { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, 158 | { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, 159 | { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, 160 | { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, 161 | { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, 162 | { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, 163 | { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, 164 | { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, 165 | { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, 166 | { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, 167 | { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, 168 | { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, 169 | { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, 170 | { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, 171 | { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, 172 | { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, 173 | { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, 174 | { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, 175 | { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, 176 | { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, 177 | { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, 178 | { 0xE0100, 0xE01EF } 179 | }; 180 | 181 | /* test for 8-bit control characters */ 182 | if (ucs == 0) 183 | return 0; 184 | if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) 185 | return -1; 186 | 187 | /* binary search in table of non-spacing characters */ 188 | if (bisearch(ucs, combining, 189 | sizeof(combining) / sizeof(struct interval) - 1)) 190 | return 0; 191 | 192 | /* if we arrive here, ucs is not a combining or C0/C1 control character */ 193 | 194 | return 1 + 195 | (ucs >= 0x1100 && 196 | (ucs <= 0x115f || /* Hangul Jamo init. consonants */ 197 | ucs == 0x2329 || ucs == 0x232a || 198 | (ucs >= 0x2e80 && ucs <= 0xa4cf && 199 | ucs != 0x303f) || /* CJK ... Yi */ 200 | (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ 201 | (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ 202 | (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ 203 | (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ 204 | (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ 205 | (ucs >= 0xffe0 && ucs <= 0xffe6) || 206 | (ucs >= 0x20000 && ucs <= 0x2fffd) || 207 | (ucs >= 0x30000 && ucs <= 0x3fffd))); 208 | } 209 | 210 | 211 | int mk_wcswidth(const char32_t* pwcs, size_t n) 212 | { 213 | int w, width = 0; 214 | 215 | for (;*pwcs && n-- > 0; pwcs++) 216 | if ((w = mk_wcwidth(*pwcs)) < 0) 217 | return -1; 218 | else 219 | width += w; 220 | 221 | return width; 222 | } 223 | 224 | 225 | /* 226 | * The following functions are the same as mk_wcwidth() and 227 | * mk_wcswidth(), except that spacing characters in the East Asian 228 | * Ambiguous (A) category as defined in Unicode Technical Report #11 229 | * have a column width of 2. This variant might be useful for users of 230 | * CJK legacy encodings who want to migrate to UCS without changing 231 | * the traditional terminal character-width behaviour. It is not 232 | * otherwise recommended for general use. 233 | */ 234 | int mk_wcwidth_cjk(wchar_t ucs) 235 | { 236 | /* sorted list of non-overlapping intervals of East Asian Ambiguous 237 | * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ 238 | static const struct interval ambiguous[] = { 239 | { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, 240 | { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, 241 | { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, 242 | { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, 243 | { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, 244 | { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, 245 | { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, 246 | { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, 247 | { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, 248 | { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, 249 | { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, 250 | { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, 251 | { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, 252 | { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, 253 | { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, 254 | { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, 255 | { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, 256 | { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, 257 | { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, 258 | { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, 259 | { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, 260 | { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, 261 | { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, 262 | { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, 263 | { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, 264 | { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, 265 | { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, 266 | { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, 267 | { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, 268 | { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, 269 | { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, 270 | { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, 271 | { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, 272 | { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, 273 | { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, 274 | { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, 275 | { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, 276 | { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, 277 | { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, 278 | { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, 279 | { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, 280 | { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, 281 | { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, 282 | { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, 283 | { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, 284 | { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, 285 | { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, 286 | { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, 287 | { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, 288 | { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, 289 | { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, 290 | { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } 291 | }; 292 | 293 | /* binary search in table of non-spacing characters */ 294 | if (bisearch(ucs, ambiguous, 295 | sizeof(ambiguous) / sizeof(struct interval) - 1)) 296 | return 2; 297 | 298 | return mk_wcwidth(ucs); 299 | } 300 | 301 | 302 | int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) 303 | { 304 | int w, width = 0; 305 | 306 | for (;*pwcs && n-- > 0; pwcs++) 307 | if ((w = mk_wcwidth_cjk(*pwcs)) < 0) 308 | return -1; 309 | else 310 | width += w; 311 | 312 | return width; 313 | } 314 | 315 | } 316 | -------------------------------------------------------------------------------- /dep/linenoise-ng/tst/example.c: -------------------------------------------------------------------------------- 1 | #include "linenoise.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static const char* examples[] = { 8 | "db", "hello", "hallo", "hans", "hansekogge", "seamann", "quetzalcoatl", "quit", "power", NULL 9 | }; 10 | 11 | void completionHook (char const* prefix, linenoiseCompletions* lc) { 12 | size_t i; 13 | 14 | for (i = 0; examples[i] != NULL; ++i) { 15 | if (strncmp(prefix, examples[i], strlen(prefix)) == 0) { 16 | linenoiseAddCompletion(lc, examples[i]); 17 | } 18 | } 19 | } 20 | 21 | int main (int argc, char** argv) { 22 | linenoiseInstallWindowChangeHandler(); 23 | 24 | while(argc > 1) { 25 | argc--; 26 | argv++; 27 | if (!strcmp(*argv, "--keycodes")) { 28 | linenoisePrintKeyCodes(); 29 | exit(0); 30 | } 31 | } 32 | 33 | const char* file = "./history"; 34 | 35 | linenoiseHistoryLoad(file); 36 | linenoiseSetCompletionCallback(completionHook); 37 | 38 | printf("starting...\n"); 39 | 40 | char const* prompt = "\x1b[1;32mlinenoise\x1b[0m> "; 41 | 42 | while (1) { 43 | char* result = linenoise(prompt); 44 | 45 | if (result == NULL) { 46 | break; 47 | } else if (!strncmp(result, "/history", 8)) { 48 | /* Display the current history. */ 49 | for (int index = 0; ; ++index) { 50 | char* hist = linenoiseHistoryLine(index); 51 | if (hist == NULL) break; 52 | printf("%4d: %s\n", index, hist); 53 | free(hist); 54 | } 55 | } 56 | if (*result == '\0') { 57 | free(result); 58 | break; 59 | } 60 | 61 | printf("thanks for the input.\n"); 62 | linenoiseHistoryAdd(result); 63 | free(result); 64 | } 65 | 66 | linenoiseHistorySave(file); 67 | linenoiseHistoryFree(); 68 | } 69 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef __config__h__ 2 | #define __config__h__ 3 | 4 | #define MATHLINE_VERSION "1.0" 5 | 6 | /* 7 | The prefix for the MathLink library is ML, while the prefix for the WSTP library is WS. This is a headache that we solve by using a prefix of MMA in the code and using defines to select the correct library at compile time. 8 | */ 9 | #if @WSTP@ == true 10 | #define MMALINK WSLINK //Special case, see below. 11 | #include "wstp.h" 12 | #define MMANAME "WSTP" 13 | #define MMANAME_LOWER "wstp" 14 | #else 15 | #define MMALINK MLINK //Not MLLINK. 16 | #include "mathlink.h" 17 | #define MMANAME "MathLink" 18 | #define MMANAME_LOWER "mathlink" 19 | #endif // @WSTP@ == true 20 | 21 | // Concatenate ML_PREFIX to NAME. 22 | #define ML_PRE(NAME) @ML_PREFIX@ ## NAME 23 | 24 | //Every MathLink/WSTP function name or type required. 25 | #define MMAInitialize ML_PRE(Initialize) 26 | #define MMADeinitialize ML_PRE(Deinitialize) 27 | #define MMAActivate ML_PRE(Activate) 28 | #define MMAClose ML_PRE(Close) 29 | #define MMAGetUTF8String ML_PRE(GetUTF8String) 30 | #define MMAGetUTF8Symbol ML_PRE(GetUTF8Symbol) 31 | #define MMAReleaseUTF8String ML_PRE(ReleaseUTF8String) 32 | #define MMANewPacket ML_PRE(NewPacket) 33 | #define MMANextPacket ML_PRE(NextPacket) 34 | #define MMAError ML_PRE(Error) 35 | #define MMAErrorMessage ML_PRE(ErrorMessage) 36 | #define MMAReleaseErrorMessage ML_PRE(ReleaseErrorMessage) 37 | #define MMAPutFunction ML_PRE(PutFunction) 38 | #define MMAPutUTF8String ML_PRE(PutUTF8String) 39 | #define MMAEndPacket ML_PRE(EndPacket) 40 | #define MMAFlush ML_PRE(Flush) 41 | #define MMAReady ML_PRE(Ready) 42 | #define MMAGetInteger ML_PRE(GetInteger) 43 | #define MMAEnvironment ML_PRE(Environment) 44 | #define MMAEOK ML_PRE(EOK) 45 | #define MMAEDEAD ML_PRE(EDEAD) 46 | #define MMAOpenArgcArgv ML_PRE(OpenArgcArgv) 47 | #define MMAWaitForLinkActivity ML_PRE(WaitForLinkActivity) 48 | 49 | #endif /* defined(__config__h__) */ 50 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic push 2 | #pragma ide diagnostic ignored "hicpp-avoid-c-arrays" 3 | // 4 | // MathLine 5 | // 6 | // Created by Robert Jacobson on 12/14/14. 7 | // Copyright (c) 2014 Robert Jacobson. All rights reserved. 8 | // 9 | 10 | #include 11 | #include "popl.hpp" 12 | #include "mlbridge.h" 13 | 14 | #define CONTINUE 0 15 | #define QUIT_WITH_SUCCESS 1 16 | #define QUIT_WITH_ERROR 2 17 | 18 | /// Copy `std::string` into an owning C-style string. 19 | char *copyDataFromString(const std::string &str){ 20 | char *new_string = new char[str.size() + 1]; 21 | std::copy(str.begin(), str.end(), new_string); 22 | new_string[str.size()] = '\0'; 23 | return new_string; 24 | } 25 | 26 | bool check_and_exit = false; 27 | 28 | int ParseProgramOptions(MLBridge &bridge, int argc, const char * argv[]){ 29 | 30 | popl::Switch helpOption("h", "help", "Produce help message."); 31 | popl::Switch checkOption("c", "check", "Establish that the connection to the kernel\nworks, then exit."); 32 | popl::Value mainloopOption("m", "mainloop", 33 | "Boolean. Whether or not to use the kernel's\nMain Loop which keeps track of session\nhistory with In[#] and Out[#] variables.\nDefaults to true.", true, &bridge.useMainLoop); 34 | popl::Value promptOption("p", "prompt", "String. The prompt presented to the user for\ninput. When inoutstrings is true, this is\ntypically the empty string.", "", &bridge.prompt); 35 | popl::Value iostringsOption("i", "inoutstrings", "Boolean. Whether or not to print the \n\"In[#]:=\" and \"Out[#]=\" strings. When\nmainloop is false this option does nothing.\nDefaults to true.", true, &bridge.showInOutStrings); 36 | popl::Value linknameOption("n", "linkname", "String. The call string to set up the link.\nDefaults to \"math -" MMANAME_LOWER "\".", "math -" MMANAME_LOWER); 37 | popl::Value linkmodeOption("l", "linkmode", "String. The " MMANAME " link mode. The default\nlaunches a new kernel which is almost\ncertainly what you want. It should be\npossible, however, to connect to a pre\nexisting kernel. Defaults to \"linklaunch\".", "linklaunch"); 38 | popl::Value getlineOption("g", "usegetline", "Boolean. If set to false, we use readline-\nlike input with command history and emacs-\nstyle editing capability. If set to true, we\nuse a simplified getline input with limited\nediting capability. Defaults to false.", false, &bridge.useGetline); 39 | popl::Value maxhistoryOption("x", "maxhistory", "Integer (nonnegative). The maximum number of\nlines to keep in the input history.Defaults\nto 10.", 10); 40 | 41 | popl::OptionParser op("MathLine Usage"); 42 | op.add(helpOption) 43 | .add(checkOption) 44 | .add(mainloopOption) 45 | .add(promptOption) 46 | .add(iostringsOption) 47 | .add(linknameOption) 48 | .add(linkmodeOption) 49 | .add(getlineOption) 50 | .add(maxhistoryOption); 51 | 52 | // Parse the options. 53 | try{ 54 | op.parse(argc, argv); 55 | }catch (std::invalid_argument &e){ 56 | std::cout << "Error: " << e.what() << ".\n"; 57 | std::cout << op << std::endl; 58 | return QUIT_WITH_ERROR; 59 | }; 60 | 61 | //Check for unknown options. 62 | if( !op.unknownOptions().empty()) { 63 | for(const auto &n : op.unknownOptions()) 64 | std::cout << "Unknown option: " << n << "\n"; 65 | std::cout << op << std::endl; 66 | return QUIT_WITH_ERROR; 67 | } 68 | //Print help message and exit. 69 | if ( helpOption.isSet() ){ 70 | std::cout << op << std::endl; 71 | return QUIT_WITH_SUCCESS; 72 | } 73 | 74 | //Now apply the options to our MLBridge instance. 75 | //(We are able to apply some automatically above.) 76 | if(checkOption.isSet()){ 77 | check_and_exit = true; 78 | } 79 | if(linknameOption.isSet()){ 80 | std::string str = linknameOption.getValue(); 81 | if(str.empty()){ 82 | //Empty string. Ignore this option. 83 | std::cout << "Option linkname cannot be empty. Ignoring." << std::endl; 84 | } else{ 85 | //Make a copy, because linknameOption will go out of scope and free the linkname before we can connect with it. 86 | bridge.argv[3] = copyDataFromString(str); 87 | } 88 | } 89 | if(linkmodeOption.isSet()){ 90 | std::string str = linkmodeOption.getValue(); 91 | if(str.empty()){ 92 | //Empty string. Ignore this option. 93 | std::cout << "Option linkmode cannot be empty. Ignoring." << std::endl; 94 | } else{ 95 | //Make a copy, because linkmodeOption will go out of scope and free the linkmode before we can connect with it. 96 | bridge.argv[1] = copyDataFromString("-" + str); 97 | } 98 | } 99 | if(maxhistoryOption.isSet()){ 100 | int max = maxhistoryOption.getValue(); 101 | if (max >= 0) { 102 | bridge.SetMaxHistory(max); 103 | } else{ 104 | std::cout << "Option maxhistory must be nonnegative. Ignoring." << std::endl; 105 | } 106 | } 107 | 108 | return CONTINUE; 109 | } 110 | 111 | int main(int argc, const char * argv[]) { 112 | //Banner 113 | std::cout << "MathLine v" MATHLINE_VERSION ": A free and open source textual interface to Mathematica." << std::endl; 114 | 115 | MLBridge bridge; 116 | 117 | //Parse the command line arguments. 118 | int parseFailed = ParseProgramOptions(bridge, argc, argv); 119 | if (CONTINUE != parseFailed) { 120 | // An unknown argument was supplied, so we exit. 121 | return parseFailed; 122 | } 123 | 124 | //Attempt to establish the MathLink connection using the options we've set. 125 | try{ 126 | bridge.Connect(); 127 | } catch(MLBridgeException &e){ 128 | std::cerr << e.ToString() << "\n"; 129 | std::cerr << "Could not connect to Mathematica. Check that " << bridge.argv[3] << " works from a command line." << std::endl; 130 | return 1; 131 | } 132 | if(bridge.IsConnected()){ 133 | //Let's print the kernel version. 134 | std::cout << "Mathematica " << bridge.GetKernelVersion() << "\n" << std::endl; 135 | if( check_and_exit ){ 136 | //Don't enter the REPL, just check and exit. 137 | std::string test = "1+2"; 138 | std::cout << bridge.kernelPrompt << test << "\n"; 139 | std::cout << bridge.GetEvaluated("1+2") << std::endl; 140 | }else{ 141 | bridge.REPL(); 142 | } 143 | 144 | } else{ 145 | std::cout << "MLBridge failed to connect."; 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | #pragma clang diagnostic pop 152 | -------------------------------------------------------------------------------- /src/mlbridge.cpp: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic push 2 | #pragma ide diagnostic ignored "hicpp-avoid-c-arrays" 3 | // 4 | // mlbridge.cpp 5 | // MathLinkBridge 6 | // 7 | // Created by Robert Jacobson on 12/14/14. 8 | // Copyright (c) 2014 Robert Jacobson. All rights reserved. 9 | // 10 | // Note: Functions with an MMA- prefix are really MathLink/WSTP functions. 11 | // The correct prefix (ML- or WS-) is determined at compile time with 12 | // a macro. See config.h for details. 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | // TODO: Handle signal interrupts correctly. 19 | //#include 20 | //TODO: Determine if stdlib is needed to free() memory linenoise allocates with malloc(). 21 | //#include 22 | #include "linenoise.h" 23 | #include "mlbridge.h" 24 | 25 | //Used for printf() debugging. 26 | bool debug = false; 27 | void DebugPrint(const std::string &msg){ 28 | //std::ostream &cout = *pcout; 29 | if(debug) std::cout << msg << "\n"; 30 | } 31 | 32 | 33 | MLBridgeException::MLBridgeException(std::string error, int errorCode): 34 | errorMsg(std::move(error)), 35 | errorCode(errorCode){ 36 | //Pass. 37 | } 38 | 39 | std::string MLBridgeException::ToString(){ 40 | return std::string(MMANAME " Error " + std::to_string(errorCode) + ": " + errorMsg); 41 | } 42 | 43 | 44 | MLBridge::MLBridge(){ 45 | SetMaxHistory(); 46 | argv = argvdefaults; 47 | } 48 | 49 | MLBridge::MLBridge(int newArgc, const char *newArgv[]){ 50 | SetMaxHistory(); 51 | argc = newArgc; 52 | argv = newArgv; 53 | } 54 | 55 | MLBridge::~MLBridge(){ 56 | Disconnect(); 57 | } 58 | 59 | void MLBridge::Connect(int newArgc, const char *newArgv[]){ 60 | argc = newArgc; 61 | argv = newArgv; 62 | Connect(); 63 | } 64 | 65 | void MLBridge::Connect(){ 66 | int error = MMAEOK; 67 | connected = false; 68 | 69 | //If no parameters are specified and this has no default parameters, bail. 70 | if(argc==0 || argv==nullptr){ 71 | throw MLBridgeException("No " MMANAME " parameters specified for the connection."); 72 | } 73 | 74 | //Initialize the link to Mathematica. 75 | environment = MMAInitialize(nullptr); 76 | if(environment == nullptr){ 77 | //Failed to initialize the link. 78 | throw MLBridgeException("Cannot initialize " MMANAME "."); 79 | } 80 | 81 | //Open the link to Mathematica. 82 | //link = MMAOpen(argc, (char **)argv); 83 | DebugPrint("Calling WSOpenArgcArgv..."); 84 | link = MMAOpenArgcArgv( environment, argc, (char **)argv, &error); 85 | DebugPrint("WSOpenArgcArgv returned: " + std::to_string(error)); 86 | if (link == nullptr || error != MMAEOK) { 87 | DebugPrint("Link is nullptr or error."); 88 | //The link failed to open. 89 | connected = false; 90 | MMADeinitialize(environment); 91 | throw MLBridgeException("Cannot open " MMANAME " link.", error); 92 | } 93 | 94 | //Activate the link. This blocks until the kernel is ready. 95 | /* 96 | TODO: MLActivate can block indefinitely. We should poll MLReady until it returns true or until we timeout before trying to call MLActivate. 97 | */ 98 | if(!MMAActivate(link)) ErrorCheck(); 99 | 100 | /* 101 | //Make sure we can connect through the link. 102 | if (MMAReady(link) != true ){ 103 | //We're not connected. 104 | connected = false; 105 | MMAClose(link); 106 | MMADeinitialize(environment); 107 | throw MLBridgeException("Cannot connect to " MMANAME "."); 108 | } 109 | */ 110 | 111 | //Link is successful. 112 | connected = true; 113 | 114 | //Initialize the kernel (sets $PrePrint, etc.). 115 | InitializeKernel(); 116 | } 117 | 118 | void MLBridge::Disconnect(){ 119 | if(connected){ 120 | MMAClose(link); 121 | connected = false; 122 | } 123 | if(environment) MMADeinitialize(environment); 124 | } 125 | 126 | void MLBridge::InitializeKernel() { 127 | int packet; 128 | 129 | //The first thing the kernel does is send us an InputNamePacket. 130 | packet = GetNextPacket(); 131 | if(packet == INPUTNAMEPKT){ 132 | kernelPrompt = GetUTF8String(); 133 | } else{ 134 | //Error condition, but not ILLEGALPKT or other link/kernel error. 135 | throw MLBridgeException("Kernel sent an unexpected packet (" + std::to_string(packet) + ") during initial startup."); 136 | } 137 | SetPrePrint("InputForm"); 138 | } 139 | 140 | std::string MLBridge::ReadInput(){ 141 | std::string input; 142 | std::string promptToUser; 143 | 144 | if(showInOutStrings){ 145 | promptToUser = prompt + kernelPrompt; 146 | } else{ 147 | promptToUser = prompt; 148 | } 149 | 150 | if(continueInput){ 151 | promptToUser.replace(0, promptToUser.length()-1, promptToUser.length(), ' '); 152 | } 153 | 154 | /* 155 | The user of this class may either use std::getline() or linenoise. The advantage of getline is that it can be used with something other than std::cin, while linenoise ignores pcin and always uses std::cin. 156 | */ 157 | if(useGetline){ 158 | std::istream &cin = *pcin; 159 | std::ostream &cout = *pcout; 160 | 161 | cout << promptToUser; 162 | 163 | std::getline(cin, input); 164 | //Check the status of cin. 165 | if (!cin.good()){ 166 | //A ctrl+c event inside of getline introduces an internal error in cin. We attempt to clear the error. 167 | /* 168 | TODO: Generally cin is std::cin (it's the default), but it need not be. We should have a more robust way of dealing with ctrl+c while blocking in getline(). 169 | */ 170 | cin.clear(); 171 | } 172 | } else { 173 | char *line; 174 | line = linenoise(promptToUser.data()); 175 | linenoiseHistoryAdd(line); 176 | input = std::string(line); 177 | free(line); 178 | } 179 | 180 | kernelPrompt = ""; 181 | return input; 182 | } 183 | 184 | void MLBridge::SetMaxHistory(int max){ 185 | //We pass max+1 because apparently 1 means zero history for linenoise. 186 | if(!linenoiseHistorySetMaxLen(max+1)) 187 | throw MLBridgeException("Invalid maximum history length."); 188 | } 189 | 190 | void MLBridge::REPL(){ 191 | std::ostream &cout = *pcout; 192 | std::string input; 193 | 194 | //For convenience we wrap everything in a try-block. However, some errors are recoverable. Though we do not do this, one could attempt to clear the error and restart the REPL. 195 | try { 196 | while(true){ 197 | input = ReadInput(); 198 | if( input == "Exit" || input == "Exit[]" || input == "Quit" ) break; 199 | Evaluate(input); 200 | //Read and act on response from the kernel. 201 | ProcessKernelResponse(); 202 | } 203 | } catch (MLBridgeException &e) { 204 | cout << e.ToString() << std::endl; 205 | } 206 | } 207 | 208 | std::string MLBridge::GetUTF8String(GetFunctionType func){ 209 | //func defaults to GetString. 210 | //MLGetUTF8String does NOT nullptr terminate the string. 211 | const unsigned char *stringBuffer = nullptr; 212 | int bytes = 0; 213 | int characters; 214 | int success = 0; 215 | std::string output; 216 | 217 | //Wait until the kernel is ready. 218 | MMAWaitForLinkActivity(link); 219 | 220 | switch(func){ 221 | case GetString: 222 | success = MMAGetUTF8String(link, &stringBuffer, &bytes, &characters); 223 | break; 224 | 225 | case GetFunction: 226 | //success = MLGetUTF8Function(link, &stringBuffer, &bytes, &characters); 227 | //Link error? At least we never use MLGetUTF8Function. 228 | break; 229 | 230 | case GetSymbol: 231 | success = MMAGetUTF8Symbol(link, &stringBuffer, &bytes, &characters); 232 | break; 233 | 234 | case GetCharacters: 235 | //We don't ever use MLGetUTF8Characters. 236 | break; 237 | } 238 | 239 | if(!success ) 240 | { 241 | //No string to read. 242 | ErrorCheck(); //Disconnects on error. 243 | throw MLBridgeException("String expected but not read from" MMANAME "."); 244 | } 245 | 246 | //Copy byte-for-byte into the output string buffer. 247 | output.assign((char *)stringBuffer, bytes); 248 | MMAReleaseUTF8String(link, stringBuffer, bytes); 249 | 250 | return output; 251 | } 252 | 253 | int MLBridge::GetNextPacket(){ 254 | int packet; 255 | 256 | //Wait until the kernel is ready. 257 | MMAWaitForLinkActivity(link); 258 | 259 | //MLNewPacket skips to the end of the current packet even if we are already at the end. It's never an error to call MLNewPacket(), but it is an error to call MLNextPacket if we aren't finished with the previous packet. 260 | if(!MMANewPacket(link)) ErrorCheck(); 261 | packet = MMANextPacket(link); 262 | if(packet == ILLEGALPKT) ErrorCheck(); 263 | 264 | return packet; 265 | } 266 | 267 | void MLBridge::ErrorCheck(){ 268 | int errorCode; 269 | std::string error; 270 | 271 | if(link == nullptr){ 272 | throw MLBridgeException("The " MMANAME " connection has been severed.", MMAEDEAD); 273 | } 274 | errorCode = MMAError(link); 275 | if(errorCode != MMAEOK){ 276 | const char *errormsg = MMAErrorMessage(link); 277 | if(errormsg){ 278 | error = std::string(errormsg); 279 | MMAReleaseErrorMessage(link, errormsg); 280 | }else{ 281 | error = "Kernel Error, but " MMANAME " did not return an error description."; 282 | } 283 | /* 284 | TO DO: Don't disconnect on error. Allow caller to attempt to recover based on error state instead. 285 | */ 286 | Disconnect(); 287 | throw MLBridgeException(error, errorCode); 288 | } 289 | } 290 | 291 | void MLBridge::Evaluate(const std::string &input){ 292 | //We record the input so that we can reference it later, for example, in the event of a syntax error. 293 | if(continueInput){ 294 | inputString.append(input); 295 | continueInput = false; 296 | } else{ 297 | inputString = input; 298 | } 299 | 300 | /* 301 | There are two kinds of strings we can send to the kernel: strings of Mathematica code (the typical case) and strings of arbitrary text (in the case of the kernel requesting user input). In addition, there are two ways to ask the kernel to process Mathematica code: as part of the "Main Loop" in which In[#] and Out[#] variables are set, etc., which is typical of a human-usable REPL, or as NOT part of the "Main Loop," which is more appropriate in cases where session history need not be accessed or retained. 302 | */ 303 | if(inputMode == ExpressionMode){ 304 | //The user has input Mathematica code. 305 | if(useMainLoop){ 306 | //Maintain session history for this evaluation. 307 | MMAPutFunction(link, "EnterTextPacket", 1); 308 | }else{ 309 | //Bypass the kernel's Main Loop. 310 | MMAPutFunction(link, "EvaluatePacket", 1); 311 | MMAPutFunction(link, "ToString", 1); 312 | MMAPutFunction(link, "ToExpression", 1); 313 | } 314 | 315 | } else if(inputMode == TextMode){ 316 | //The user has input arbitrary text, from example in response to an InputString[] call. 317 | MMAPutFunction(link, "TextPacket", 1); 318 | //Turn off TextMode 319 | inputMode = ExpressionMode; 320 | } 321 | MMAPutUTF8String(link, (const unsigned char *)inputString.data(), (int)inputString.size()); 322 | MMAEndPacket(link); 323 | //We check for errors after sending a packet. 324 | ErrorCheck(); 325 | running = true; 326 | } 327 | 328 | void MLBridge::EvaluateWithoutMainLoop(const std::string &input, bool eatReturnPacket){ 329 | // This function always circumvents the kernel's Main Loop. Use it for setting $PrePrint for example. 330 | 331 | if(!IsConnected()){ 332 | throw MLBridgeException("Tried to evaluate without being connected to a kernel."); 333 | } 334 | 335 | //We record the input so that we can reference it later, for example, in the event of a syntax error. 336 | if(continueInput){ 337 | inputString.append(input); 338 | continueInput = false; 339 | } else{ 340 | inputString = input; 341 | } 342 | 343 | //Bypass the kernel's Main Loop. 344 | MMAPutFunction(link, "EvaluatePacket", 1); 345 | MMAPutFunction(link, "ToString", 1); 346 | MMAPutFunction(link, "ToExpression", 1); 347 | MMAPutUTF8String(link, (const unsigned char *)inputString.data(), (int)inputString.size()); 348 | MMAEndPacket(link); 349 | //We check for errors after sending a packet. 350 | ErrorCheck(); 351 | 352 | //The default is to discard the result. 353 | if(eatReturnPacket){ 354 | GetNextPacket(); 355 | } else{ 356 | running = true; 357 | } 358 | } 359 | 360 | void MLBridge::SetPrePrint(const std::string &preprintfunction){ 361 | EvaluateWithoutMainLoop("$PrePrint = " + preprintfunction); 362 | } 363 | 364 | std::string MLBridge::GetKernelVersion() { 365 | return GetEvaluated("$Version"); 366 | } 367 | 368 | std::string MLBridge::GetEvaluated(const std::string &expression){ 369 | 370 | EvaluateWithoutMainLoop(expression, false); 371 | //EvaluateWithoutMainLoop sets running to true, but we get the return string ourselves, so we leaving running = false; 372 | running = false; 373 | 374 | 375 | //Fetch the string returned by the kernel. 376 | if(GetNextPacket() != RETURNPKT){ 377 | //Return an error. 378 | throw MLBridgeException("Kernel sent an unexpected packet in response to a request to evaluate $Version."); 379 | } 380 | 381 | return GetUTF8String(); 382 | } 383 | 384 | bool MLBridge::IsRunning(){ 385 | //If we never started, there's nothing to do! 386 | if(!running) return false; 387 | 388 | if(!MMAFlush(link) || !MMAReady(link)) { 389 | //Check if an error has occurred. 390 | ErrorCheck(); 391 | running = true; 392 | } else{ 393 | running = false; 394 | } 395 | return running; 396 | } 397 | 398 | void MLBridge::PrintMessages(){ 399 | std::ostream &cout = *pcout; 400 | MLBridgeMessage *m; 401 | 402 | while(!messages.empty()){ 403 | //Only print if we aren't continuing previous input. 404 | if(!continueInput){ 405 | m = messages.front(); 406 | cout << "\n" << m->message << std::endl; 407 | if(m->position > -1){ 408 | cout << inputString << "\n"; 409 | cout << std::string(m->position, '.'); 410 | cout << "^ Syntax Error.\n" << std::endl; 411 | } 412 | 413 | delete m; 414 | } 415 | messages.pop(); 416 | } 417 | } 418 | 419 | //The following represent In[#]:= strings and text that the kernel prints to the console respectively. 420 | bool MLBridge::ReceivedInputNamePacket(){ 421 | 422 | DebugPrint(""); 423 | if(!continueInput) *pcout << "\n\n"; 424 | if(showInOutStrings && useMainLoop){ 425 | kernelPrompt = GetUTF8String(); 426 | } 427 | 428 | return true; 429 | } 430 | 431 | //Represents a prompt for user input. 432 | bool MLBridge::ReceivedInputPacket(){ 433 | 434 | DebugPrint(""); 435 | kernelPrompt = GetUTF8String(); 436 | 437 | return true; 438 | } 439 | 440 | //The following represent Out[#]= strings and text that the kernel prints to the console respectively. 441 | bool MLBridge::ReceivedOutputNamePacket(){ 442 | std::ostream &cout = *pcout; 443 | std::string output; 444 | 445 | DebugPrint(""); 446 | 447 | //Print any cached messages. 448 | PrintMessages(); 449 | 450 | cout << "\n"; 451 | if(showInOutStrings){ 452 | outputPrompt = GetUTF8String(); 453 | cout << outputPrompt; 454 | } 455 | 456 | return false; 457 | } 458 | 459 | //Contains the text returned from the evaluation. 460 | bool MLBridge::ReceivedReturnTextPacket(){ 461 | std::ostream &cout = *pcout; 462 | 463 | DebugPrint(""); 464 | 465 | //Frankly, I'm not sure how to correctly format the output without starting to print it on a new line. There must be a way because Wolfram's interface does it. 466 | cout << "\n" << GetUTF8String(); 467 | 468 | return false; 469 | } 470 | 471 | //We receive these packets when useMainLoop is false. 472 | bool MLBridge::ReceivedReturnExpressionPacket(){ 473 | std::ostream &cout = *pcout; 474 | 475 | DebugPrint(""); 476 | 477 | //Print any cached messages. 478 | PrintMessages(); 479 | 480 | cout << GetUTF8String() << std::endl; 481 | 482 | //If we are using the Main Loop, we expect more packets from the kernel, so we keep done=false. 483 | return !useMainLoop; 484 | } 485 | 486 | //This packet typically contains the message (string) describing a syntax error. 487 | bool MLBridge::ReceivedTextPacket(){ 488 | std::ostream &cout = *pcout; 489 | 490 | DebugPrint(""); 491 | 492 | //We don't print if this text packet is for incomplete input syntax error. 493 | if(!continueInput){ 494 | cout << GetUTF8String(); 495 | } 496 | 497 | return false; 498 | } 499 | 500 | //This packet contains postscript code, i.e., the kernel is sending an image. 501 | bool MLBridge::ReceivedDisplayPacket(){ 502 | DebugPrint(""); 503 | 504 | if(makeNewImage){ 505 | //If this is a new image (i.e. postscript string), create a new string to store the code. 506 | makeNewImage = false; 507 | image = new std::string(); 508 | 509 | //If we want to include our own postscript preamble, this is where it would go. 510 | } 511 | 512 | image->append(GetUTF8String()); 513 | 514 | return false; 515 | } 516 | 517 | //This packet is received when the kernel is done sending postscript code. 518 | bool MLBridge::ReceivedDisplayEndPacket(){ 519 | DebugPrint(""); 520 | 521 | image->append(GetUTF8String()); 522 | 523 | //If we want to include our own postscript "post-amble", this is where it would go. 524 | images.push(*image); 525 | image = nullptr; 526 | makeNewImage = true; 527 | 528 | //It is unclear if we still return false when useMainLoop is false. 529 | return false; 530 | } 531 | 532 | //The kernel reports a syntax error. This packet contains the position of the error. 533 | bool MLBridge::ReceivedSyntaxPacket(){ 534 | DebugPrint(""); 535 | 536 | //We cache syntax messages. This syntax packet must be associated to the last message cached. Record the position in that message's cache entry. 537 | MLBridgeMessage *m = messages.back(); 538 | MMAGetInteger(link, &m->position); 539 | 540 | /* 541 | We don't throw an MLBridgeException because it's for errors associated to the link to the kernel, not for every possible error. Thus we do not throw an exception here. In fact, doing so would disrupt the internal state of the REPL. If one wishes to catch syntax errors, the best way is probably to implement a call-back function to handle them and call the function from here. 542 | */ 543 | return false; 544 | } 545 | 546 | //This packet represents a request from the kernel for a plaintext string input from the user. 547 | bool MLBridge::ReceivedInputStringPacket(){ 548 | std::ostream &cout = *pcout; 549 | 550 | DebugPrint(""); 551 | 552 | cout << GetUTF8String(); 553 | 554 | inputMode = TextMode; 555 | return true; 556 | } 557 | 558 | //Sending an interrupt signal (i.e. ctrl+c) brings up the Interrupt> menu which expects textual user input. 559 | bool MLBridge::ReceivedMenuPacket(){ 560 | std::ostream &cout = *pcout; 561 | DebugPrint(""); 562 | 563 | //What is this number? It seems to indicate that the kernel will subsequently output additional menu text, so we should expect it. (I think.) This happens when the user enters an invalid option at the Interrupt> menu. 564 | int interruptMenuNumber = 0; 565 | 566 | MMAGetInteger(link, &interruptMenuNumber); 567 | 568 | kernelPrompt = GetUTF8String(); 569 | 570 | //The Interrupt> menu wants text input. 571 | inputMode = TextMode; 572 | 573 | //If we are getting a text menu... 574 | if(interruptMenuNumber == 0){ 575 | //We are about to receive additional menu text from the kernel. 576 | if(GetNextPacket() != TEXTPKT){ 577 | //We received a packet we didn't expect. 578 | throw MLBridgeException("Menu text expected but not received."); 579 | } 580 | DebugPrint(""); 581 | 582 | //Get the menu text from the text packet and print it. 583 | cout << GetUTF8String(); 584 | 585 | } else{ 586 | //Start on a new line. 587 | cout << "\n"; 588 | } 589 | 590 | return true; 591 | } 592 | 593 | //Message from the kernel, for example, to indicate a runtime error. 594 | bool MLBridge::ReceivedMessagePacket(){ 595 | std::ostream &cout = *pcout; 596 | 597 | DebugPrint(""); 598 | 599 | /* 600 | The problem we need to solve here is that we want to silently continue reading input from the user if we receive "Syntax::sntxi", but otherwise we want to output the message. Problem is, we might receive other messages BEFORE "Syntax::sntxi". So we cache syntax messsages until they are all read from the kernel, check for the presence of "Syntax::sntxi", and print them or not. 601 | 602 | Is it true that we only ever get "Syntax" messages prior to "Syntax::sntxi"? If not, then the following code needs to be adjusted. 603 | */ 604 | MLBridgeMessage *message; 605 | std::string symbolName = GetUTF8String(GetSymbol); 606 | std::string tag = GetUTF8String(); 607 | 608 | //Syntax::sntxi: 609 | if(symbolName == "Syntax"){ 610 | if(tag == "sntxi"){ 611 | //Keep reading input. 612 | continueInput = true; 613 | } 614 | //Make a new message to stash. 615 | message = new MLBridgeMessage; 616 | message->name = symbolName; 617 | message->tag = tag; 618 | //Setting position = -1 indicates that there is no associated error position with this message. 619 | message->position = -1; 620 | 621 | //Get the text of the message from the kernel. 622 | GetNextPacket(); 623 | 624 | message->message = GetUTF8String(); 625 | 626 | //Stash the message. 627 | messages.push(message); 628 | } else if(!continueInput){ 629 | //It's not a "Syntax::" message, and we haven't gotten a "Syntax::sntxi:" message, so go ahead and print any messages we've cached. 630 | PrintMessages(); 631 | 632 | //Now get the text of this message from the kernel and print it. 633 | GetNextPacket(); 634 | cout << "\n" << GetUTF8String() << std::endl; 635 | } 636 | return false; 637 | } 638 | 639 | //The SuspendPacket tells this link that something else, perhaps another front end, has taken control, and thus the kernel will start ignoring us. 640 | bool MLBridge::ReceivedSuspendPacket(){ 641 | DebugPrint(""); 642 | 643 | MMANewPacket(link); //Do I need this line? 644 | 645 | *pcout << "--suspended--" << std::endl; 646 | 647 | return true; 648 | } 649 | 650 | //The ResumePacket tells this link that something else, perhaps another front end, has given us back control, and thus the kernel will start paying attention to us again. 651 | bool MLBridge::ReceivedResumePacket(){ 652 | DebugPrint(""); 653 | 654 | *pcout << "--resumed--" << std::endl; 655 | 656 | MMANewPacket(link); //Do I need this line? 657 | 658 | return false; 659 | } 660 | 661 | //A dialog is entered when a computation is interrupted and the user enters debug mode. Debug modes/dialogs can be nested. 662 | bool MLBridge::ReceivedBeginDialogPacket(){ 663 | DebugPrint(""); 664 | 665 | int dialogLevel; 666 | MMAGetInteger(link, &dialogLevel); 667 | *pcout << "entering dialog:" << dialogLevel << std::endl; 668 | 669 | return false; 670 | } 671 | 672 | bool MLBridge::ReceivedEndDialogPacket(){ 673 | DebugPrint(""); 674 | 675 | int dialogLevel; 676 | MMAGetInteger(link, &dialogLevel); 677 | dialogLevel--; 678 | *pcout << "leaving dialog:" << dialogLevel << std::endl; 679 | 680 | return false; 681 | } 682 | 683 | void MLBridge::ProcessKernelResponse() { 684 | bool done = false; 685 | std::string output; 686 | 687 | //Keep fetching packets until the kernel is finished responding. 688 | do { 689 | //We poll to see if MLNextPacket will block. If it will, just return true. We don't want to spend time blocking in MLNextPacket because we want to be able to send an MLInterruptMessage if we need to. 690 | while(IsRunning() ); 691 | 692 | //Get the next packet. 693 | int packet = GetNextPacket(); 694 | 695 | // TODO: Received*Packet() returns a bool indicating whether or not the loop in this method should continue. That's stupid. Those bools should exist in the case statements themselves, and Received*Packet() should just process the received packet. 696 | switch (packet) { 697 | case INPUTNAMEPKT: 698 | done = ReceivedInputNamePacket(); 699 | break; 700 | case INPUTPKT: 701 | done = ReceivedInputPacket(); 702 | break; 703 | case OUTPUTNAMEPKT: 704 | done = ReceivedOutputNamePacket(); 705 | break; 706 | case RETURNTEXTPKT: 707 | done = ReceivedReturnTextPacket(); 708 | break; 709 | case RETURNPKT: 710 | //Handled by RETURNEXPRPKT. 711 | case RETURNEXPRPKT: 712 | done = ReceivedReturnExpressionPacket(); 713 | break; 714 | case TEXTPKT: 715 | done = ReceivedTextPacket(); 716 | break; 717 | case MESSAGEPKT: 718 | done = ReceivedMessagePacket(); 719 | break; 720 | case SYNTAXPKT: 721 | done = ReceivedSyntaxPacket(); 722 | break; 723 | case INPUTSTRPKT: 724 | done = ReceivedInputStringPacket(); 725 | break; 726 | case MENUPKT: 727 | done = ReceivedMenuPacket(); 728 | break; 729 | case DISPLAYPKT: 730 | done = ReceivedDisplayPacket(); 731 | break; 732 | case DISPLAYENDPKT: 733 | done = ReceivedDisplayEndPacket(); 734 | break; 735 | case SUSPENDPKT: 736 | done = ReceivedSuspendPacket(); 737 | break; 738 | case RESUMEPKT: 739 | done = ReceivedResumePacket(); 740 | break; 741 | case BEGINDLGPKT: 742 | done = ReceivedBeginDialogPacket(); 743 | break; 744 | case ENDDLGPKT: 745 | done = ReceivedEndDialogPacket(); 746 | break; 747 | case ILLEGALPKT: 748 | DebugPrint(""); 749 | //We should attempt recovery. 750 | done = true; 751 | break; 752 | default: //Unhandled packet. 753 | DebugPrint(""); 754 | break; 755 | } //End switch. 756 | 757 | //Print any cached messages we haven't printed yet. 758 | if (done) PrintMessages(); 759 | 760 | running = !done; 761 | }while(!done); 762 | 763 | return; 764 | } 765 | 766 | #pragma clang diagnostic pop 767 | -------------------------------------------------------------------------------- /src/mlbridge.h: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic push 2 | #pragma ide diagnostic ignored "hicpp-avoid-c-arrays" 3 | // 4 | // mlbridge.h 5 | // MathLinkBridge 6 | // 7 | // Created by Robert Jacobson on 12/14/14. 8 | // Copyright (c) 2014 Robert Jacobson. All rights reserved. 9 | // 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "config.h" 19 | 20 | struct MLBridgeException: public std::exception{ 21 | MLBridgeException(std::string errorMsg, int errorCode = 0); 22 | int errorCode; 23 | std::string errorMsg; 24 | std::string ToString(); 25 | }; 26 | 27 | struct MLBridgeMessage{ 28 | std::string name; 29 | std::string tag; 30 | std::string message; 31 | int position; 32 | }; 33 | 34 | class MLBridge { 35 | public: 36 | //Parameters affecting how to communicate with the user and kernel. 37 | std::string prompt{""}; //Supplied by the user of this MLBridge object. 38 | std::string kernelPrompt{""}; //Supplied by the kernel. 39 | bool useMainLoop = true; 40 | bool showInOutStrings = true; 41 | bool useGetline = false; 42 | 43 | int argc = 4; 44 | const char *argvdefaults[4] = {"MathLine", 45 | "-linklaunch", 46 | "-linkname", 47 | "math -" MMANAME_LOWER}; 48 | const char **argv = nullptr; 49 | //Streams to use for io. 50 | std::ostream *pcout = &std::cout; 51 | std::istream *pcin = &std::cin; 52 | /* 53 | The kernel may return postscript code. This vector of postscript strings contains the postscript images returned by the kernel. It is up to the user of MLBridge to remove items from this vector. 54 | 55 | To use this feature you must evaluate '$Display = "stdout"'. 56 | 57 | */ 58 | std::queue images; 59 | 60 | 61 | MLBridge(); 62 | MLBridge(int argc, const char *argv[]); 63 | ~MLBridge(); 64 | 65 | void Connect(int argc, const char *argv[]); 66 | void Connect(); 67 | bool IsConnected(){ return connected; } 68 | void Disconnect(); 69 | 70 | bool IsRunning(); 71 | void REPL(); 72 | void SetMaxHistory(int max = 10); 73 | void SetPrePrint(const std::string &preprintfunction); 74 | std::string GetKernelVersion(); 75 | std::string GetEvaluated(const std::string &expression); 76 | 77 | private: 78 | //Variables to keep track of state. 79 | bool connected = false; 80 | bool running = false; 81 | //The following is set to true when an "incomplete expression" syntax error occurs, as it indicates that more input is needed. 82 | bool continueInput = false; 83 | enum {ExpressionMode, TextMode} inputMode = ExpressionMode; 84 | //Holds the image data (postscript code) as we receive it from the kernel until we have it all. 85 | std::string *image = nullptr; 86 | bool makeNewImage = true; 87 | //The last input string we sent to the kernel. 88 | std::string inputString; 89 | std::string outputPrompt; 90 | //Syntax messages are cached. 91 | std::queue messages; 92 | 93 | MMALINK link = nullptr; 94 | MMAEnvironment environment = nullptr; 95 | 96 | void ErrorCheck(); 97 | std::string ReadInput(); 98 | void PrintMessages(); 99 | void InitializeKernel(); 100 | 101 | // Evaluation with REPL. 102 | void Evaluate(const std::string &input); 103 | //Skips the Main Loop regardless of the state of useMainLoop. 104 | void EvaluateWithoutMainLoop(const std::string &input, bool eatReturnPacket = true); 105 | 106 | //Convenience wrapper for MLGetUTF8String, etc.. 107 | enum GetFunctionType {GetString, GetFunction, GetSymbol, GetCharacters}; 108 | std::string GetUTF8String(GetFunctionType func = GetString); 109 | int GetNextPacket(); 110 | 111 | //These are the packets this code knows how to handle. Each returns whether no more packets are expected from the kernel. 112 | bool ReceivedInputNamePacket(); 113 | bool ReceivedInputPacket(); 114 | bool ReceivedOutputNamePacket(); 115 | bool ReceivedReturnTextPacket(); 116 | //bool ReceivedReturnPacket(); //We just use the code for ReturnExpressionPacket for this case. 117 | bool ReceivedReturnExpressionPacket(); 118 | bool ReceivedTextPacket(); 119 | bool ReceivedDisplayPacket(); 120 | bool ReceivedDisplayEndPacket(); 121 | bool ReceivedSyntaxPacket(); 122 | bool ReceivedInputStringPacket(); 123 | bool ReceivedMenuPacket(); 124 | bool ReceivedMessagePacket(); 125 | bool ReceivedSuspendPacket(); 126 | bool ReceivedResumePacket(); 127 | bool ReceivedBeginDialogPacket(); 128 | bool ReceivedEndDialogPacket(); 129 | 130 | void ProcessKernelResponse(); 131 | }; 132 | 133 | #pragma clang diagnostic pop 134 | -------------------------------------------------------------------------------- /src/popl.hpp: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of popl (program options parser lib) 3 | Copyright (C) 2015-2016 Johannes Pohl 4 | 5 | This software may be modified and distributed under the terms 6 | of the MIT license. See the LICENSE file for details. 7 | ***/ 8 | 9 | 10 | #ifndef POPL_H 11 | #define POPL_H 12 | 13 | #define NOMINMAX 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | namespace popl 25 | { 26 | 27 | #define POPL_VERSION "0.3.0" 28 | 29 | 30 | enum // permitted values for its `has_arg' field... 31 | { 32 | no_argument = 0, // option never takes an argument 33 | required_argument, // option always requires an argument 34 | optional_argument // option may take an argument 35 | }; 36 | 37 | 38 | class Option 39 | { 40 | friend class OptionParser; 41 | public: 42 | Option(const std::string& shortOption, const std::string& longOption, const std::string& description); 43 | 44 | char getShortOption() const; 45 | std::string getLongOption() const; 46 | std::string getDescription() const; 47 | unsigned int count() const; 48 | bool isSet() const; 49 | 50 | protected: 51 | virtual void parse(const std::string& whatOption, const char* value) = 0; 52 | virtual void updateReference(); 53 | virtual std::string optionToString() const; 54 | virtual std::vector descriptionToString(size_t width = 40) const; 55 | virtual int hasArg() const = 0; 56 | 57 | std::string shortOption_; 58 | std::string longOption_; 59 | std::string description_; 60 | unsigned int count_; 61 | }; 62 | 63 | 64 | 65 | 66 | template 67 | class Value : public Option 68 | { 69 | public: 70 | Value(const std::string& shortOption, const std::string& longOption, const std::string& description); 71 | Value(const std::string& shortOption, const std::string& longOption, const std::string& description, const T& defaultVal, T* assignTo = NULL); 72 | 73 | Value& assignTo(T* var); 74 | Value& setDefault(const T& value); 75 | T getValue(size_t idx = 0) const; 76 | 77 | protected: 78 | virtual void parse(const std::string& whatOption, const char* value); 79 | virtual std::string optionToString() const; 80 | virtual int hasArg() const; 81 | virtual void addValue(const T& value); 82 | virtual void updateReference(); 83 | T* assignTo_; 84 | std::vector values_; 85 | T default_; 86 | bool hasDefault_; 87 | }; 88 | 89 | 90 | 91 | 92 | template 93 | class Implicit : public Value 94 | { 95 | public: 96 | Implicit(const std::string& shortOption, const std::string& longOption, const std::string& description, const T& implicitVal); 97 | Implicit(const std::string& shortOption, const std::string& longOption, const std::string& description, const T& implicitVal, T* assignTo = NULL); 98 | 99 | Value& assignTo(T* var); 100 | 101 | protected: 102 | virtual void parse(const std::string& whatOption, const char* value); 103 | virtual std::string optionToString() const; 104 | virtual int hasArg() const; 105 | Value& setDefault(const T& value); 106 | }; 107 | 108 | 109 | 110 | 111 | class Switch : public Value 112 | { 113 | public: 114 | Switch(const std::string& shortOption, const std::string& longOption, const std::string& description); 115 | Switch(const std::string& shortOption, const std::string& longOption, const std::string& description, bool* assignTo); 116 | 117 | protected: 118 | virtual void parse(const std::string& whatOption, const char* value); 119 | virtual std::string optionToString() const; 120 | virtual int hasArg() const; 121 | Switch& setDefault(const bool& value); 122 | }; 123 | 124 | 125 | 126 | 127 | class OptionParser 128 | { 129 | public: 130 | OptionParser(const std::string& description = ""); 131 | virtual ~OptionParser(); 132 | OptionParser& add(Option& option); 133 | void parse(int argc, const char **argv); 134 | std::string help() const; 135 | const std::vector& options() const; 136 | const std::vector& nonOptionArgs() const; 137 | const std::vector& unknownOptions() const; 138 | 139 | protected: 140 | std::vector options_; 141 | std::string description_; 142 | std::vector nonOptionArgs_; 143 | std::vector unknownOptions_; 144 | 145 | Option* getLongOpt(const std::string& opt) const; 146 | Option* getShortOpt(char opt) const; 147 | }; 148 | 149 | 150 | 151 | 152 | 153 | /// Option implementation ///////////////////////////////// 154 | 155 | Option::Option(const std::string& shortOption, const std::string& longOption, const std::string& description) : 156 | shortOption_(shortOption), 157 | longOption_(longOption), 158 | description_(description), 159 | count_(0) 160 | { 161 | if (shortOption.size() > 1) 162 | throw std::invalid_argument("length of short option must be <= 1: '" + shortOption + "'"); 163 | 164 | if (shortOption.empty() && longOption.empty()) 165 | throw std::invalid_argument("short and long option are empty"); 166 | } 167 | 168 | 169 | void Option::updateReference() 170 | { 171 | } 172 | 173 | 174 | char Option::getShortOption() const 175 | { 176 | if (!shortOption_.empty()) 177 | return shortOption_[0]; 178 | return 0; 179 | } 180 | 181 | 182 | std::string Option::getLongOption() const 183 | { 184 | return longOption_; 185 | } 186 | 187 | 188 | std::string Option::getDescription() const 189 | { 190 | return description_; 191 | } 192 | 193 | 194 | unsigned int Option::count() const 195 | { 196 | return count_; 197 | } 198 | 199 | 200 | bool Option::isSet() const 201 | { 202 | return (count() > 0); 203 | } 204 | 205 | 206 | std::string Option::optionToString() const 207 | { 208 | std::stringstream line; 209 | if (getShortOption() != 0) 210 | { 211 | line << " -" << getShortOption(); 212 | if (!getLongOption().empty()) 213 | line << ", "; 214 | } 215 | else 216 | line << " "; 217 | 218 | if (!getLongOption().empty()) 219 | line << "--" << getLongOption(); 220 | 221 | return line.str(); 222 | } 223 | 224 | 225 | std::vector Option::descriptionToString(size_t width) const 226 | { 227 | std::vector lines; 228 | std::stringstream description(getDescription()); 229 | std::string line; 230 | while (std::getline(description, line, '\n')) 231 | lines.push_back(line); 232 | 233 | return lines; 234 | } 235 | 236 | 237 | 238 | 239 | 240 | /// Value implementation ///////////////////////////////// 241 | 242 | template 243 | Value::Value(const std::string& shortOption, const std::string& longOption, const std::string& description) : 244 | Option(shortOption, longOption, description), 245 | assignTo_(NULL), 246 | hasDefault_(false) 247 | { 248 | } 249 | 250 | 251 | template 252 | Value::Value(const std::string& shortOption, const std::string& longOption, const std::string& description, const T& defaultVal, T* assignTo) : 253 | Option(shortOption, longOption, description), 254 | assignTo_(assignTo), 255 | default_(defaultVal), 256 | hasDefault_(true) 257 | { 258 | updateReference(); 259 | } 260 | 261 | 262 | template 263 | Value& Value::assignTo(T* var) 264 | { 265 | assignTo_ = var; 266 | return *this; 267 | } 268 | 269 | 270 | template 271 | Value& Value::setDefault(const T& value) 272 | { 273 | default_ = value; 274 | hasDefault_ = true; 275 | return *this; 276 | } 277 | 278 | 279 | template 280 | void Value::updateReference() 281 | { 282 | if (assignTo_ != NULL) 283 | { 284 | if (isSet() || hasDefault_) 285 | *assignTo_ = getValue(); 286 | } 287 | } 288 | 289 | 290 | template 291 | void Value::addValue(const T& value) 292 | { 293 | values_.push_back(value); 294 | ++count_; 295 | updateReference(); 296 | } 297 | 298 | 299 | template 300 | T Value::getValue(size_t idx) const 301 | { 302 | if (!isSet()) 303 | { 304 | if (hasDefault_) 305 | return default_; 306 | else 307 | { 308 | std::stringstream optionStr; 309 | if (getShortOption() != 0) 310 | optionStr << "-" << getShortOption(); 311 | else 312 | optionStr << "--" << getLongOption(); 313 | 314 | throw std::out_of_range("option not set: \"" + optionStr.str() + "\""); 315 | } 316 | } 317 | 318 | if (idx >= count_) 319 | { 320 | std::stringstream optionStr; 321 | optionStr << "index out of range (" << idx << ") for \""; 322 | if (getShortOption() != 0) 323 | optionStr << "-" << getShortOption(); 324 | else 325 | optionStr << "--" << getLongOption(); 326 | optionStr << "\""; 327 | throw std::out_of_range(optionStr.str()); 328 | } 329 | 330 | return values_[idx]; 331 | } 332 | 333 | 334 | template 335 | int Value::hasArg() const 336 | { 337 | return required_argument; 338 | } 339 | 340 | 341 | template<> 342 | void Value::parse(const std::string& whatOption, const char* value) 343 | { 344 | if (strlen(value) == 0) 345 | throw std::invalid_argument("missing argument for " + whatOption); 346 | 347 | addValue(value); 348 | } 349 | 350 | 351 | template 352 | void Value::parse(const std::string& whatOption, const char* value) 353 | { 354 | T parsedValue; 355 | std::string strValue; 356 | if (value != NULL) 357 | strValue = value; 358 | 359 | std::istringstream is(strValue); 360 | int valuesRead = 0; 361 | while (is.good()) 362 | { 363 | if (is.peek() != EOF) 364 | is >> std::boolalpha >> parsedValue; 365 | else 366 | break; 367 | 368 | valuesRead++; 369 | } 370 | 371 | if (is.fail()) 372 | throw std::invalid_argument("invalid argument for " + whatOption + ": '" + strValue + "'"); 373 | 374 | if (valuesRead > 1) 375 | throw std::invalid_argument("too many arguments for " + whatOption + ": '" + strValue + "'"); 376 | 377 | if (strValue.empty()) 378 | throw std::invalid_argument("missing argument for " + whatOption); 379 | 380 | addValue(parsedValue); 381 | } 382 | 383 | 384 | template 385 | std::string Value::optionToString() const 386 | { 387 | std::stringstream ss; 388 | ss << Option::optionToString() << " arg"; 389 | if (hasDefault_) 390 | { 391 | std::stringstream defaultStr; 392 | defaultStr << std::boolalpha << default_; 393 | if (!defaultStr.str().empty()) 394 | ss << " (=" << defaultStr.str() << ")"; 395 | } 396 | return ss.str(); 397 | } 398 | 399 | 400 | 401 | 402 | 403 | /// Implicit implementation ///////////////////////////////// 404 | 405 | template 406 | Implicit::Implicit(const std::string& shortOption, const std::string& longOption, const std::string& description, const T& implicitVal) : 407 | Value(shortOption, longOption, description, implicitVal) 408 | { 409 | } 410 | 411 | 412 | template 413 | Implicit::Implicit(const std::string& shortOption, const std::string& longOption, const std::string& description, const T& implicitVal, T* assignTo) : 414 | Value(shortOption, longOption, description, implicitVal, assignTo) 415 | { 416 | } 417 | 418 | 419 | template 420 | int Implicit::hasArg() const 421 | { 422 | return optional_argument; 423 | } 424 | 425 | 426 | template 427 | void Implicit::parse(const std::string& whatOption, const char* value) 428 | { 429 | if ((value != NULL) && (strlen(value) > 0)) 430 | Value::parse(whatOption, value); 431 | else 432 | this->addValue(this->default_); 433 | } 434 | 435 | 436 | template 437 | std::string Implicit::optionToString() const 438 | { 439 | std::stringstream ss; 440 | ss << Option::optionToString() << " [=arg(=" << std::boolalpha << this->default_ << ")]"; 441 | return ss.str(); 442 | } 443 | 444 | 445 | 446 | 447 | 448 | /// Switch implementation ///////////////////////////////// 449 | 450 | Switch::Switch(const std::string& shortOption, const std::string& longOption, const std::string& description) : 451 | Value(shortOption, longOption, description, false) 452 | { 453 | } 454 | 455 | 456 | Switch::Switch(const std::string& shortOption, const std::string& longOption, const std::string& description, bool* assignTo) : 457 | Value(shortOption, longOption, description, false, assignTo) 458 | { 459 | } 460 | 461 | 462 | void Switch::parse(const std::string& whatOption, const char* value) 463 | { 464 | addValue(true); 465 | } 466 | 467 | 468 | int Switch::hasArg() const 469 | { 470 | return no_argument; 471 | } 472 | 473 | 474 | std::string Switch::optionToString() const 475 | { 476 | return Option::optionToString(); 477 | } 478 | 479 | 480 | 481 | 482 | 483 | /// OptionParser implementation ///////////////////////////////// 484 | 485 | OptionParser::OptionParser(const std::string& description) : description_(description) 486 | { 487 | } 488 | 489 | 490 | OptionParser::~OptionParser() 491 | { 492 | } 493 | 494 | 495 | OptionParser& OptionParser::add(Option& option) 496 | { 497 | for (size_t n=0; ngetShortOption())) 500 | throw std::invalid_argument("dublicate short option '-" + std::string(1, option.getShortOption()) + "'"); 501 | if (!option.getLongOption().empty() && (option.getLongOption() == (options_[n]->getLongOption()))) 502 | throw std::invalid_argument("dublicate long option '--" + option.getLongOption() + "'"); 503 | } 504 | options_.push_back(&option); 505 | return *this; 506 | } 507 | 508 | 509 | const std::vector& OptionParser::options() const 510 | { 511 | return options_; 512 | } 513 | 514 | 515 | const std::vector& OptionParser::nonOptionArgs() const 516 | { 517 | return nonOptionArgs_; 518 | } 519 | 520 | 521 | const std::vector& OptionParser::unknownOptions() const 522 | { 523 | return unknownOptions_; 524 | } 525 | 526 | 527 | std::string OptionParser::help() const 528 | { 529 | std::stringstream s; 530 | if (!description_.empty()) 531 | s << description_ << ":\n"; 532 | 533 | size_t optionRightMargin(20); 534 | const size_t maxDescriptionLeftMargin(40); 535 | // const size_t descriptionRightMargin(80); 536 | 537 | for (size_t opt = 0; opt < options_.size(); ++opt) 538 | optionRightMargin = std::max(optionRightMargin, options_[opt]->optionToString().size() + 2); 539 | optionRightMargin = std::min(maxDescriptionLeftMargin - 2, optionRightMargin); 540 | 541 | for (size_t opt = 0; opt < options_.size(); ++opt) 542 | { 543 | std::string optionStr = options_[opt]->optionToString(); 544 | if (optionStr.size() < optionRightMargin) 545 | optionStr.resize(optionRightMargin, ' '); 546 | else 547 | optionStr += "\n" + std::string(optionRightMargin, ' '); 548 | s << optionStr; 549 | 550 | std::vector lines = options_[opt]->descriptionToString(20); 551 | std::string empty(optionRightMargin, ' '); 552 | for (size_t n=0; n 0) 555 | s << "\n" << empty; 556 | s << lines[n]; 557 | } 558 | if(opt !=options_.size()-1) s << "\n\n"; 559 | } 560 | 561 | return s.str(); 562 | } 563 | 564 | 565 | Option* OptionParser::getLongOpt(const std::string& opt) const 566 | { 567 | for (size_t n = 0; n < options_.size(); ++n) 568 | { 569 | if (options_[n]->getLongOption() == opt) 570 | return options_[n]; 571 | } 572 | return NULL; 573 | } 574 | 575 | 576 | Option* OptionParser::getShortOpt(char opt) const 577 | { 578 | for (size_t n = 0; n < options_.size(); ++n) 579 | if (options_[n]->getShortOption() == opt) 580 | return options_[n]; 581 | return NULL; 582 | } 583 | 584 | 585 | void OptionParser::parse(int argc, const char **argv) 586 | { 587 | unknownOptions_.clear(); 588 | nonOptionArgs_.clear(); 589 | for (int n=1; nhasArg() == no_argument) 616 | { 617 | if (!optarg.empty()) 618 | option = NULL; 619 | } 620 | else if (option->hasArg() == required_argument) 621 | { 622 | if (optarg.empty() && n < argc-1) 623 | optarg = argv[++n]; 624 | } 625 | } 626 | 627 | if (option != NULL) 628 | option->parse(opt, optarg.c_str()); 629 | else 630 | unknownOptions_.push_back(arg); 631 | } 632 | else if (arg.find("-") == 0) 633 | { 634 | /// short option arg 635 | std::string opt = arg.substr(1); 636 | bool unknown = false; 637 | for (size_t m=0; mhasArg() == required_argument) 646 | { 647 | /// use the rest of the current argument as optarg 648 | optarg = opt.substr(m + 1); 649 | /// or the next arg 650 | if (optarg.empty() && n < argc-1) 651 | optarg = argv[++n]; 652 | m = opt.size(); 653 | } 654 | else if (option->hasArg() == optional_argument) 655 | { 656 | /// use the rest of the current argument as optarg 657 | optarg = opt.substr(m + 1); 658 | m = opt.size(); 659 | } 660 | } 661 | 662 | if (option != NULL) 663 | option->parse(std::string(1, c), optarg.c_str()); 664 | else 665 | unknown = true; 666 | } 667 | if (unknown) 668 | unknownOptions_.push_back(arg); 669 | } 670 | else 671 | { 672 | nonOptionArgs_.push_back(arg); 673 | } 674 | } 675 | } 676 | 677 | 678 | std::ostream& operator<<(std::ostream& out, const OptionParser& op) 679 | { 680 | out << op.help(); 681 | return out; 682 | } 683 | 684 | 685 | } // namespace popl 686 | 687 | #endif 688 | 689 | 690 | --------------------------------------------------------------------------------