From f766acad62cd5cf7ed701a9521db2f2a96039778 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 16 Oct 2013 22:51:51 -0400 Subject: [PATCH] drop the linenoise library Closes #5038 --- .gitattributes | 1 - configure | 2 +- mk/platform.mk | 2 +- mk/rt.mk | 5 +- mk/tests.mk | 3 - src/README.txt | 1 - src/libextra/extra.rs | 1 - src/libextra/rl.rs | 143 --- src/rt/linenoise/README.markdown | 47 - src/rt/linenoise/example.c | 30 - src/rt/linenoise/linenoise.c | 1581 ---------------------------- src/rt/linenoise/linenoise.h | 61 -- src/rt/linenoise/utf8.c | 115 -- src/rt/linenoise/utf8.h | 79 -- src/rt/rust_builtin.cpp | 12 - src/rt/rustrt.def.in | 9 - src/test/auxiliary/issue_3882.rc | 15 - src/test/auxiliary/issue_3882.rs | 21 - src/test/run-pass/issue_3882.rs | 16 - src/test/run-pass/rl-human-test.rs | 83 -- 20 files changed, 3 insertions(+), 2224 deletions(-) delete mode 100644 src/libextra/rl.rs delete mode 100644 src/rt/linenoise/README.markdown delete mode 100644 src/rt/linenoise/example.c delete mode 100644 src/rt/linenoise/linenoise.c delete mode 100644 src/rt/linenoise/linenoise.h delete mode 100644 src/rt/linenoise/utf8.c delete mode 100644 src/rt/linenoise/utf8.h delete mode 100644 src/test/auxiliary/issue_3882.rc delete mode 100644 src/test/auxiliary/issue_3882.rs delete mode 100644 src/test/run-pass/issue_3882.rs delete mode 100644 src/test/run-pass/rl-human-test.rs diff --git a/.gitattributes b/.gitattributes index 57f8083a556..3e368ce70e9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,5 +7,4 @@ src/etc/pkg/rust-logo.ico binary src/rt/msvc/* -whitespace src/rt/vg/* -whitespace -src/rt/linenoise/* -whitespace src/rt/jemalloc/**/* -whitespace diff --git a/configure b/configure index c2629324d80..eb934c1807f 100755 --- a/configure +++ b/configure @@ -686,7 +686,7 @@ do make_dir $t/rt/libuv/src/ev make_dir $t/rt/jemalloc for i in \ - isaac linenoise sync test \ + isaac sync test \ arch/i386 arch/x86_64 arch/arm arch/mips \ sundown/src sundown/html do diff --git a/mk/platform.mk b/mk/platform.mk index 5885a57f69b..8f9714e62d5 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -29,7 +29,7 @@ $(foreach t,$(CFG_TARGET_TRIPLES),$(info cfg: os for $(t) is $(OSTYPE_$(t)))) # FIXME: no-omit-frame-pointer is just so that task_start_wrapper # has a frame pointer and the stack walker can understand it. Turning off # frame pointers everywhere is overkill -CFG_GCCISH_CFLAGS += -fno-omit-frame-pointer -DUSE_UTF8 +CFG_GCCISH_CFLAGS += -fno-omit-frame-pointer # On Darwin, we need to run dsymutil so the debugging information ends # up in the right place. On other platforms, it automatically gets diff --git a/mk/rt.mk b/mk/rt.mk index 412e25bed4d..347c32f1720 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -96,9 +96,7 @@ RUNTIME_CXXS_$(1)_$(2) := \ rt/rust_android_dummy.cpp \ rt/rust_test_helpers.cpp -RUNTIME_CS_$(1)_$(2) := rt/linenoise/linenoise.c \ - rt/linenoise/utf8.c \ - rt/sundown/src/autolink.c \ +RUNTIME_CS_$(1)_$(2) := rt/sundown/src/autolink.c \ rt/sundown/src/buffer.c \ rt/sundown/src/stack.c \ rt/sundown/src/markdown.c \ @@ -116,7 +114,6 @@ RT_BUILD_DIR_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/stage$(2) RUNTIME_DEF_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/rustrt$$(CFG_DEF_SUFFIX_$(1)) RUNTIME_INCS_$(1)_$(2) := -I $$(S)src/rt -I $$(S)src/rt/isaac -I $$(S)src/rt/uthash \ -I $$(S)src/rt/arch/$$(HOST_$(1)) \ - -I $$(S)src/rt/linenoise \ -I $$(S)src/rt/sundown/src \ -I $$(S)src/rt/sundown/html \ -I $$(S)src/libuv/include diff --git a/mk/tests.mk b/mk/tests.mk index fb5bd094404..9991aacc949 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -227,7 +227,6 @@ ALL_CS := $(wildcard $(S)src/rt/*.cpp \ $(S)src/rt/*/*/*.cpp \ $(S)src/rustllvm/*.cpp) ALL_CS := $(filter-out $(S)src/rt/miniz.cpp \ - $(wildcard $(S)src/rt/linenoise/*.c) \ $(wildcard $(S)src/rt/sundown/src/*.c) \ $(wildcard $(S)src/rt/sundown/html/*.c) \ ,$(ALL_CS)) @@ -240,8 +239,6 @@ ALL_HS := $(filter-out $(S)src/rt/vg/valgrind.h \ $(S)src/rt/msvc/typeof.h \ $(S)src/rt/msvc/stdint.h \ $(S)src/rt/msvc/inttypes.h \ - $(S)src/rt/linenoise/linenoise.h \ - $(S)src/rt/linenoise/utf8.h \ $(wildcard $(S)src/rt/sundown/src/*.h) \ $(wildcard $(S)src/rt/sundown/html/*.h) \ ,$(ALL_HS)) diff --git a/src/README.txt b/src/README.txt index 7cf523e1017..1ee08247c73 100644 --- a/src/README.txt +++ b/src/README.txt @@ -17,7 +17,6 @@ rt/sync - Concurrency utils rt/util - Small utility classes for the runtime. rt/vg - Valgrind headers rt/msvc - MSVC support -rt/linenoise - a readline-like line editing library test/ Testsuite test/compile-fail - Tests that should fail to compile diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs index 3cea76b8edd..4b29709895e 100644 --- a/src/libextra/extra.rs +++ b/src/libextra/extra.rs @@ -94,7 +94,6 @@ pub mod term; pub mod time; pub mod arena; pub mod base64; -pub mod rl; pub mod workcache; pub mod enum_set; #[path="num/bigint.rs"] diff --git a/src/libextra/rl.rs b/src/libextra/rl.rs deleted file mode 100644 index c8a17451a70..00000000000 --- a/src/libextra/rl.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Bindings for the ability to read lines of input from the console - -use std::c_str::ToCStr; -use std::libc::{c_char, c_int}; -use std::{local_data, str, rt}; -use std::unstable::finally::Finally; - -mod rustrt { - use std::libc::{c_char, c_int}; - - externfn!(fn linenoise(prompt: *c_char) -> *c_char) - externfn!(fn linenoiseHistoryAdd(line: *c_char) -> c_int) - externfn!(fn linenoiseHistorySetMaxLen(len: c_int) -> c_int) - externfn!(fn linenoiseHistorySave(file: *c_char) -> c_int) - externfn!(fn linenoiseHistoryLoad(file: *c_char) -> c_int) - externfn!(fn linenoiseSetCompletionCallback(callback: extern "C" fn(*i8, *()))) - externfn!(fn linenoiseAddCompletion(completions: *(), line: *c_char)) - - externfn!(fn rust_take_linenoise_lock()) - externfn!(fn rust_drop_linenoise_lock()) -} - -macro_rules! locked { - ($expr:expr) => { - { - // FIXME #9105: can't use a static mutex in pure Rust yet. - rustrt::rust_take_linenoise_lock(); - let x = $expr; - rustrt::rust_drop_linenoise_lock(); - x - } - } -} - -/// Add a line to history -pub fn add_history(line: &str) -> bool { - do line.with_c_str |buf| { - unsafe { - (locked!(rustrt::linenoiseHistoryAdd(buf))) == 1 as c_int - } - } -} - -/// Set the maximum amount of lines stored -pub fn set_history_max_len(len: int) -> bool { - unsafe { - (locked!(rustrt::linenoiseHistorySetMaxLen(len as c_int))) == 1 - as c_int - } -} - -/// Save line history to a file -pub fn save_history(file: &str) -> bool { - do file.with_c_str |buf| { - // 0 on success, -1 on failure - unsafe { - (locked!(rustrt::linenoiseHistorySave(buf))) == 0 as c_int - } - } -} - -/// Load line history from a file -pub fn load_history(file: &str) -> bool { - do file.with_c_str |buf| { - // 0 on success, -1 on failure - unsafe { - (locked!(rustrt::linenoiseHistoryLoad(buf))) == 0 as c_int - } - } -} - -/// Print out a prompt and then wait for input and return it -pub fn read(prompt: &str) -> Option<~str> { - do prompt.with_c_str |buf| { - let line = unsafe { - locked!(rustrt::linenoise(buf)) - }; - - if line.is_null() { None } - else { - unsafe { - do (|| { - Some(str::raw::from_c_str(line)) - }).finally { - // linenoise's return value is from strdup, so we - // better not leak it. - rt::global_heap::exchange_free(line); - } - } - } - } -} - -/// The callback used to perform completions. -pub trait CompletionCb { - /// Performs a completion. - fn complete(&self, line: ~str, suggestion: &fn(~str)); -} - -local_data_key!(complete_key: @CompletionCb) - -/// Bind to the main completion callback in the current task. -/// -/// The completion callback should not call any `extra::rl` functions -/// other than the closure that it receives as its second -/// argument. Calling such a function will deadlock on the mutex used -/// to ensure that the calls are thread-safe. -pub unsafe fn complete(cb: @CompletionCb) { - local_data::set(complete_key, cb); - - extern fn callback(line: *c_char, completions: *()) { - do local_data::get(complete_key) |opt_cb| { - // only fetch completions if a completion handler has been - // registered in the current task. - match opt_cb { - None => {} - Some(cb) => { - unsafe { - do cb.complete(str::raw::from_c_str(line)) - |suggestion| { - do suggestion.with_c_str |buf| { - rustrt::linenoiseAddCompletion(completions, - buf); - } - } - } - } - } - } - } - - locked!(rustrt::linenoiseSetCompletionCallback(callback)); -} diff --git a/src/rt/linenoise/README.markdown b/src/rt/linenoise/README.markdown deleted file mode 100644 index f008d2d3d5e..00000000000 --- a/src/rt/linenoise/README.markdown +++ /dev/null @@ -1,47 +0,0 @@ -# Linenoise - -A minimal, zero-config, BSD licensed, readline replacement. - -News: linenoise now includes minimal completion support, thanks to Pieter Noordhuis (@pnoordhuis). - -News: linenoise is now part of [Android](http://android.git.kernel.org/?p=platform/system/core.git;a=tree;f=liblinenoise;h=56450eaed7f783760e5e6a5993ef75cde2e29dea;hb=HEAD Android)! - -## Can a line editing library be 20k lines of code? - -Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing? - -So what usually happens is either: - - * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh). - * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance). - -The result is a pollution of binaries without line editing support. - -So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporing line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to linenoise if not. - -## Terminals, in 2010. - -Apparently almost every terminal you can happen to use today has some kind of support for VT100 alike escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it. - -Since it's so young I guess there are a few bugs, or the lib may not compile or work with some operating system, but it's a matter of a few weeks and eventually we'll get it right, and there will be no excuses for not shipping command line tools without built-in line editing support. - -The library is currently less than 400 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software. - -## Tested with... - - * Linux text only console ($TERM = linux) - * Linux KDE terminal application ($TERM = xterm) - * Linux xterm ($TERM = xterm) - * Mac OS X iTerm ($TERM = xterm) - * Mac OS X default Terminal.app ($TERM = xterm) - * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) - * IBM AIX 6.1 - * FreeBSD xterm ($TERM = xterm) - -Please test it everywhere you can and report back! - -## Let's push this forward! - -Please fork it and add something interesting and send me a pull request. What's especially interesting are fixes, new key bindings, completion. - -Send feedbacks to antirez at gmail diff --git a/src/rt/linenoise/example.c b/src/rt/linenoise/example.c deleted file mode 100644 index cb51a0af8f9..00000000000 --- a/src/rt/linenoise/example.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include "linenoise.h" - -#ifndef NO_COMPLETION -void completion(const char *buf, linenoiseCompletions *lc) { - if (buf[0] == 'h') { - linenoiseAddCompletion(lc,"hello"); - linenoiseAddCompletion(lc,"hello there"); - } -} -#endif - -int main(void) { - char *line; - -#ifndef NO_COMPLETION - linenoiseSetCompletionCallback(completion); -#endif - linenoiseHistoryLoad("history.txt"); /* Load the history at startup */ - while((line = linenoise("hello> ")) != NULL) { - if (line[0] != '\0') { - printf("echo: '%s'\n", line); - linenoiseHistoryAdd(line); - linenoiseHistorySave("history.txt"); /* Save every new entry */ - } - free(line); - } - return 0; -} diff --git a/src/rt/linenoise/linenoise.c b/src/rt/linenoise/linenoise.c deleted file mode 100644 index 0ce4d559bed..00000000000 --- a/src/rt/linenoise/linenoise.c +++ /dev/null @@ -1,1581 +0,0 @@ -/* linenoise.c -- guerrilla line editing library against the idea that a - * line editing lib needs to be 20,000 lines of C code. - * - * You can find the latest source code at: - * - * http://github.com/msteveb/linenoise - * (forked from http://github.com/antirez/linenoise) - * - * Does a number of crazy assumptions that happen to be true in 99.9999% of - * the 2010 UNIX computers around. - * - * ------------------------------------------------------------------------ - * - * Copyright (c) 2010, Salvatore Sanfilippo - * Copyright (c) 2010, Pieter Noordhuis - * Copyright (c) 2011, Steve Bennett - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ------------------------------------------------------------------------ - * - * References: - * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html - * - * Bloat: - * - Completion? - * - * Unix/termios - * ------------ - * List of escape sequences used by this program, we do everything just - * a few sequences. In order to be so cheap we may have some - * flickering effect with some slow terminal, but the lesser sequences - * the more compatible. - * - * EL (Erase Line) - * Sequence: ESC [ n K - * Effect: if n is 0 or missing, clear from cursor to end of line - * Effect: if n is 1, clear from beginning of line to cursor - * Effect: if n is 2, clear entire line - * - * CUF (CUrsor Forward) - * Sequence: ESC [ n C - * Effect: moves cursor forward of n chars - * - * CR (Carriage Return) - * Sequence: \r - * Effect: moves cursor to column 1 - * - * The following are used to clear the screen: ESC [ H ESC [ 2 J - * This is actually composed of two sequences: - * - * cursorhome - * Sequence: ESC [ H - * Effect: moves the cursor to upper left corner - * - * ED2 (Clear entire screen) - * Sequence: ESC [ 2 J - * Effect: clear the whole screen - * - * == For highlighting control characters, we also use the following two == - * SO (enter StandOut) - * Sequence: ESC [ 7 m - * Effect: Uses some standout mode such as reverse video - * - * SE (Standout End) - * Sequence: ESC [ 0 m - * Effect: Exit standout mode - * - * == Only used if TIOCGWINSZ fails == - * DSR/CPR (Report cursor position) - * Sequence: ESC [ 6 n - * Effect: reports current cursor position as ESC [ NNN ; MMM R - * - * win32/console - * ------------- - * If __MINGW32__ is defined, the win32 console API is used. - * This could probably be made to work for the msvc compiler too. - * This support based in part on work by Jon Griffiths. - */ - -#ifdef _WIN32 /* Windows platform, either MinGW or Visual Studio (MSVC) */ -#include -#include -#define USE_WINCONSOLE -#ifdef __MINGW32__ -#define HAVE_UNISTD_H -#else -/* Microsoft headers don't like old POSIX names */ -#define strdup _strdup -#define snprintf _snprintf -#endif -#else -#include -#include -#include -#define USE_TERMIOS -#define HAVE_UNISTD_H -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include "linenoise.h" -#include "utf8.h" - -#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 -#define LINENOISE_MAX_LINE 4096 - -#define ctrl(C) ((C) - '@') - -/* Use -ve numbers here to co-exist with normal unicode chars */ -enum { - SPECIAL_NONE, - SPECIAL_UP = -20, - SPECIAL_DOWN = -21, - SPECIAL_LEFT = -22, - SPECIAL_RIGHT = -23, - SPECIAL_DELETE = -24, - SPECIAL_HOME = -25, - SPECIAL_END = -26, - SPECIAL_INSERT = -27, - SPECIAL_PAGE_UP = -28, - SPECIAL_PAGE_DOWN = -29 -}; - -static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; -static int history_len = 0; -static char **history = NULL; - -/* Structure to contain the status of the current (being edited) line */ -struct current { - char *buf; /* Current buffer. Always null terminated */ - int bufmax; /* Size of the buffer, including space for the null termination */ - int len; /* Number of bytes in 'buf' */ - int chars; /* Number of chars in 'buf' (utf-8 chars) */ - int pos; /* Cursor position, measured in chars */ - int cols; /* Size of the window, in chars */ - const char *prompt; - char *capture; /* Allocated capture buffer, or NULL for none. Always null terminated */ -#if defined(USE_TERMIOS) - int fd; /* Terminal fd */ -#elif defined(USE_WINCONSOLE) - HANDLE outh; /* Console output handle */ - HANDLE inh; /* Console input handle */ - int rows; /* Screen rows */ - int x; /* Current column during output */ - int y; /* Current row */ -#endif -}; - -static int fd_read(struct current *current); -static int getWindowSize(struct current *current); - -void linenoiseHistoryFree(void) { - if (history) { - int j; - - for (j = 0; j < history_len; j++) - free(history[j]); - free(history); - history = NULL; - history_len = 0; - } -} - -#if defined(USE_TERMIOS) -static void linenoiseAtExit(void); -static struct termios orig_termios; /* in order to restore at exit */ -static int rawmode = 0; /* for atexit() function to check if restore is needed*/ -static int atexit_registered = 0; /* register atexit just 1 time */ - -static const char *unsupported_term[] = {"dumb","cons25",NULL}; - -static int isUnsupportedTerm(void) { - char *term = getenv("TERM"); - - if (term) { - int j; - for (j = 0; unsupported_term[j]; j++) { - if (strcasecmp(term, unsupported_term[j]) == 0) { - return 1; - } - } - } - return 0; -} - -static int enableRawMode(struct current *current) { - struct termios raw; - - current->fd = STDIN_FILENO; - - if (!isatty(current->fd) || isUnsupportedTerm() || - tcgetattr(current->fd, &orig_termios) == -1) { -fatal: - errno = ENOTTY; - return -1; - } - - if (!atexit_registered) { - atexit(linenoiseAtExit); - atexit_registered = 1; - } - - raw = orig_termios; /* modify the original mode */ - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. - * We want read to return every single byte, without timeout. */ - raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ - - /* put terminal in raw mode after flushing */ - if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) { - goto fatal; - } - rawmode = 1; - - current->cols = 0; - return 0; -} - -static void disableRawMode(struct current *current) { - /* Don't even check the return value as it's too late. */ - if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1) - rawmode = 0; -} - -/* At exit we'll try to fix the terminal to the initial conditions. */ -static void linenoiseAtExit(void) { - if (rawmode) { - tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios); - } - linenoiseHistoryFree(); -} - -/* gcc/glibc insists that we care about the return code of write! - * Clarification: This means that a void-cast like "(void) (EXPR)" - * does not work. - */ -#define IGNORE_RC(EXPR) if (EXPR) {} - -/* This is fdprintf() on some systems, but use a different - * name to avoid conflicts - */ -static void fd_printf(int fd, const char *format, ...) -{ - va_list args; - char buf[64]; - int n; - - va_start(args, format); - n = vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - IGNORE_RC(write(fd, buf, n)); -} - -static void clearScreen(struct current *current) -{ - fd_printf(current->fd, "\x1b[H\x1b[2J"); -} - -static void cursorToLeft(struct current *current) -{ - fd_printf(current->fd, "\r"); -} - -static int outputChars(struct current *current, const char *buf, int len) -{ - return write(current->fd, buf, len); -} - -static void outputControlChar(struct current *current, char ch) -{ - fd_printf(current->fd, "\x1b[7m^%c\x1b[0m", ch); -} - -static void eraseEol(struct current *current) -{ - fd_printf(current->fd, "\x1b[0K"); -} - -static void setCursorPos(struct current *current, int x) -{ - fd_printf(current->fd, "\r\x1b[%dC", x); -} - -/** - * Reads a char from 'fd', waiting at most 'timeout' milliseconds. - * - * A timeout of -1 means to wait forever. - * - * Returns -1 if no char is received within the time or an error occurs. - */ -static int fd_read_char(int fd, int timeout) -{ - struct pollfd p; - unsigned char c; - - p.fd = fd; - p.events = POLLIN; - - if (poll(&p, 1, timeout) == 0) { - /* timeout */ - return -1; - } - if (read(fd, &c, 1) != 1) { - return -1; - } - return c; -} - -/** - * Reads a complete utf-8 character - * and returns the unicode value, or -1 on error. - */ -static int fd_read(struct current *current) -{ -#ifdef USE_UTF8 - char buf[4]; - int n; - int i; - int c; - - if (read(current->fd, &buf[0], 1) != 1) { - return -1; - } - n = utf8_charlen(buf[0]); - if (n < 1 || n > 3) { - return -1; - } - for (i = 1; i < n; i++) { - if (read(current->fd, &buf[i], 1) != 1) { - return -1; - } - } - buf[n] = 0; - /* decode and return the character */ - utf8_tounicode(buf, &c); - return c; -#else - return fd_read_char(current->fd, -1); -#endif -} - -static int countColorControlChars(const char* prompt, int plen) -{ - /* ANSI color control sequences have the form: - * "\x1b" "[" [0-9;]+ "m" - * We parse them with a simple state machine. - */ - - enum { - search_esc, - expect_bracket, - expect_inner, - expect_trail - } state = search_esc; - int len = 0, found = 0; - char ch; - - /* XXX: Strictly we should be checking utf8 chars rather than - * bytes in case of the extremely unlikely scenario where - * an ANSI sequence is part of a utf8 sequence. - */ - for (; plen ; plen--, prompt++) { - ch = *prompt; - - switch (state) { - case search_esc: - len = 0; - if (ch == '\x1b') { - state = expect_bracket; - len++; - } - break; - case expect_bracket: - if (ch == '[') { - state = expect_inner; - len++; - } else { - state = search_esc; - } - break; - case expect_inner: - if (ch >= '0' && ch <= '9') { - len++; - state = expect_trail; - } else { - state = search_esc; - } - break; - case expect_trail: - if (ch == 'm') { - len++; - found += len; - state = search_esc; - } else if ((ch != ';') && ((ch < '0') || (ch > '9'))) { - state = search_esc; - } - /* 0-9, or semicolon */ - len++; - break; - } - } - - return found; -} - -static int getWindowSize(struct current *current) -{ - struct winsize ws; - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) { - current->cols = ws.ws_col; - return 0; - } - - /* Failed to query the window size. Perhaps we are on a serial terminal. - * Try to query the width by sending the cursor as far to the right - * and reading back the cursor position. - * Note that this is only done once per call to linenoise rather than - * every time the line is refreshed for efficiency reasons. - */ - if (current->cols == 0) { - current->cols = 80; - - /* Move cursor far right and report cursor position, then back to the left */ - fd_printf(current->fd, "\x1b[999C" "\x1b[6n"); - - /* Parse the response: ESC [ rows ; cols R */ - if (fd_read_char(current->fd, 100) == 0x1b && fd_read_char(current->fd, 100) == '[') { - int n = 0; - while (1) { - int ch = fd_read_char(current->fd, 100); - if (ch == ';') { - /* Ignore rows */ - n = 0; - } - else if (ch == 'R') { - /* Got cols */ - if (n != 0 && n < 1000) { - current->cols = n; - } - break; - } - else if (ch >= 0 && ch <= '9') { - n = n * 10 + ch - '0'; - } - else { - break; - } - } - } - } - return 0; -} - -/** - * If escape (27) was received, reads subsequent - * chars to determine if this is a known special key. - * - * Returns SPECIAL_NONE if unrecognised, or -1 if EOF. - * - * If no additional char is received within a short time, - * 27 is returned. - */ -static int check_special(int fd) -{ - int c = fd_read_char(fd, 50); - int c2; - - if (c < 0) { - return 27; - } - - c2 = fd_read_char(fd, 50); - if (c2 < 0) { - return c2; - } - if (c == '[' || c == 'O') { - /* Potential arrow key */ - switch (c2) { - case 'A': - return SPECIAL_UP; - case 'B': - return SPECIAL_DOWN; - case 'C': - return SPECIAL_RIGHT; - case 'D': - return SPECIAL_LEFT; - case 'F': - return SPECIAL_END; - case 'H': - return SPECIAL_HOME; - } - } - if (c == '[' && c2 >= '1' && c2 <= '8') { - /* extended escape */ - c = fd_read_char(fd, 50); - if (c == '~') { - switch (c2) { - case '2': - return SPECIAL_INSERT; - case '3': - return SPECIAL_DELETE; - case '5': - return SPECIAL_PAGE_UP; - case '6': - return SPECIAL_PAGE_DOWN; - case '7': - return SPECIAL_HOME; - case '8': - return SPECIAL_END; - } - } - while (c != -1 && c != '~') { - /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */ - c = fd_read_char(fd, 50); - } - } - - return SPECIAL_NONE; -} -#elif defined(USE_WINCONSOLE) - -static DWORD orig_consolemode = 0; - -static int enableRawMode(struct current *current) { - DWORD n; - INPUT_RECORD irec; - - current->outh = GetStdHandle(STD_OUTPUT_HANDLE); - current->inh = GetStdHandle(STD_INPUT_HANDLE); - - if (!PeekConsoleInput(current->inh, &irec, 1, &n)) { - return -1; - } - if (getWindowSize(current) != 0) { - return -1; - } - if (GetConsoleMode(current->inh, &orig_consolemode)) { - SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT); - } - return 0; -} - -static void disableRawMode(struct current *current) -{ - SetConsoleMode(current->inh, orig_consolemode); -} - -static void clearScreen(struct current *current) -{ - COORD topleft = { 0, 0 }; - DWORD n; - - FillConsoleOutputCharacter(current->outh, ' ', - current->cols * current->rows, topleft, &n); - FillConsoleOutputAttribute(current->outh, - FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, - current->cols * current->rows, topleft, &n); - SetConsoleCursorPosition(current->outh, topleft); -} - -static void cursorToLeft(struct current *current) -{ - COORD pos = { 0, (SHORT)current->y }; - DWORD n; - - FillConsoleOutputAttribute(current->outh, - FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n); - current->x = 0; -} - -static int outputChars(struct current *current, const char *buf, int len) -{ - COORD pos = { (SHORT)current->x, (SHORT)current->y }; - DWORD n; - - WriteConsoleOutputCharacter(current->outh, buf, len, pos, &n); - current->x += len; - return 0; -} - -static void outputControlChar(struct current *current, char ch) -{ - COORD pos = { (SHORT)current->x, (SHORT)current->y }; - DWORD n; - - FillConsoleOutputAttribute(current->outh, BACKGROUND_INTENSITY, 2, pos, &n); - outputChars(current, "^", 1); - outputChars(current, &ch, 1); -} - -static void eraseEol(struct current *current) -{ - COORD pos = { (SHORT)current->x, (SHORT)current->y }; - DWORD n; - - FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n); -} - -static void setCursorPos(struct current *current, int x) -{ - COORD pos = { (SHORT)x, (SHORT)current->y }; - - SetConsoleCursorPosition(current->outh, pos); - current->x = x; -} - -static int fd_read(struct current *current) -{ - while (1) { - INPUT_RECORD irec; - DWORD n; - if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) { - break; - } - if (!ReadConsoleInput (current->inh, &irec, 1, &n)) { - break; - } - if (irec.EventType == KEY_EVENT && irec.Event.KeyEvent.bKeyDown) { - KEY_EVENT_RECORD *k = &irec.Event.KeyEvent; - if (k->dwControlKeyState & ENHANCED_KEY) { - switch (k->wVirtualKeyCode) { - case VK_LEFT: - return SPECIAL_LEFT; - case VK_RIGHT: - return SPECIAL_RIGHT; - case VK_UP: - return SPECIAL_UP; - case VK_DOWN: - return SPECIAL_DOWN; - case VK_INSERT: - return SPECIAL_INSERT; - case VK_DELETE: - return SPECIAL_DELETE; - case VK_HOME: - return SPECIAL_HOME; - case VK_END: - return SPECIAL_END; - case VK_PRIOR: - return SPECIAL_PAGE_UP; - case VK_NEXT: - return SPECIAL_PAGE_DOWN; - } - } - /* Note that control characters are already translated in AsciiChar */ - else { -#ifdef USE_UTF8 - return k->uChar.UnicodeChar; -#else - return k->uChar.AsciiChar; -#endif - } - } - } - return -1; -} - -static int countColorControlChars(const char* prompt, int plen) -{ - /* For windows we assume that there are no embedded ansi color - * control sequences. - */ - return 0; -} - -static int getWindowSize(struct current *current) -{ - CONSOLE_SCREEN_BUFFER_INFO info; - if (!GetConsoleScreenBufferInfo(current->outh, &info)) { - return -1; - } - current->cols = info.dwSize.X; - current->rows = info.dwSize.Y; - if (current->cols <= 0 || current->rows <= 0) { - current->cols = 80; - return -1; - } - current->y = info.dwCursorPosition.Y; - current->x = info.dwCursorPosition.X; - return 0; -} -#endif - -static int utf8_getchars(char *buf, int c) -{ -#ifdef USE_UTF8 - return utf8_fromunicode(buf, c); -#else - *buf = c; - return 1; -#endif -} - -/** - * Returns the unicode character at the given offset, - * or -1 if none. - */ -static int get_char(struct current *current, int pos) -{ - if (pos >= 0 && pos < current->chars) { - int c; - int i = utf8_index(current->buf, pos); - (void)utf8_tounicode(current->buf + i, &c); - return c; - } - return -1; -} - -static void refreshLine(const char *prompt, struct current *current) -{ - int plen; - int pchars; - int backup = 0; - int i; - const char *buf = current->buf; - int chars = current->chars; - int pos = current->pos; - int b; - int ch; - int n; - - /* Should intercept SIGWINCH. For now, just get the size every time */ - getWindowSize(current); - - plen = strlen(prompt); - pchars = utf8_strlen(prompt, plen); - - /* Scan the prompt for embedded ansi color control sequences and - * discount them as characters/columns. - */ - pchars -= countColorControlChars(prompt, plen); - - /* Account for a line which is too long to fit in the window. - * Note that control chars require an extra column - */ - - /* How many cols are required to the left of 'pos'? - * The prompt, plus one extra for each control char - */ - n = pchars + utf8_strlen(buf, current->len); - b = 0; - for (i = 0; i < pos; i++) { - b += utf8_tounicode(buf + b, &ch); - if (ch < ' ') { - n++; - } - } - - /* If too many are needed, strip chars off the front of 'buf' - * until it fits. Note that if the current char is a control character, - * we need one extra col. - */ - if (current->pos < current->chars && get_char(current, current->pos) < ' ') { - n++; - } - - while (n >= current->cols && pos > 0) { - b = utf8_tounicode(buf, &ch); - if (ch < ' ') { - n--; - } - n--; - buf += b; - pos--; - chars--; - } - - /* Cursor to left edge, then the prompt */ - cursorToLeft(current); - outputChars(current, prompt, plen); - - /* Now the current buffer content */ - - /* Need special handling for control characters. - * If we hit 'cols', stop. - */ - b = 0; /* unwritted bytes */ - n = 0; /* How many control chars were written */ - for (i = 0; i < chars; i++) { - int ch; - int w = utf8_tounicode(buf + b, &ch); - if (ch < ' ') { - n++; - } - if (pchars + i + n >= current->cols) { - break; - } - if (ch < ' ') { - /* A control character, so write the buffer so far */ - outputChars(current, buf, b); - buf += b + w; - b = 0; - outputControlChar(current, ch + '@'); - if (i < pos) { - backup++; - } - } - else { - b += w; - } - } - outputChars(current, buf, b); - - /* Erase to right, move cursor to original position */ - eraseEol(current); - setCursorPos(current, pos + pchars + backup); -} - -static void set_current(struct current *current, const char *str) -{ - strncpy(current->buf, str, current->bufmax); - current->buf[current->bufmax - 1] = 0; - current->len = strlen(current->buf); - current->pos = current->chars = utf8_strlen(current->buf, current->len); -} - -static int has_room(struct current *current, int bytes) -{ - return current->len + bytes < current->bufmax - 1; -} - -/** - * Removes the char at 'pos'. - * - * Returns 1 if the line needs to be refreshed, 2 if not - * and 0 if nothing was removed - */ -static int remove_char(struct current *current, int pos) -{ - if (pos >= 0 && pos < current->chars) { - int p1, p2; - int ret = 1; - p1 = utf8_index(current->buf, pos); - p2 = p1 + utf8_index(current->buf + p1, 1); - -#ifdef USE_TERMIOS - /* optimise remove char in the case of removing the last char */ - if (current->pos == pos + 1 && current->pos == current->chars) { - if (current->buf[pos] >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) { - ret = 2; - fd_printf(current->fd, "\b \b"); - } - } -#endif - - /* Move the null char too */ - memmove(current->buf + p1, current->buf + p2, current->len - p2 + 1); - current->len -= (p2 - p1); - current->chars--; - - if (current->pos > pos) { - current->pos--; - } - return ret; - } - return 0; -} - -/** - * Insert 'ch' at position 'pos' - * - * Returns 1 if the line needs to be refreshed, 2 if not - * and 0 if nothing was inserted (no room) - */ -static int insert_char(struct current *current, int pos, int ch) -{ - char buf[3]; - int n = utf8_getchars(buf, ch); - - if (has_room(current, n) && pos >= 0 && pos <= current->chars) { - int p1, p2; - int ret = 1; - p1 = utf8_index(current->buf, pos); - p2 = p1 + n; - -#ifdef USE_TERMIOS - /* optimise the case where adding a single char to the end and no scrolling is needed */ - if (current->pos == pos && current->chars == pos) { - if (ch >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) { - IGNORE_RC(write(current->fd, buf, n)); - ret = 2; - } - } -#endif - - memmove(current->buf + p2, current->buf + p1, current->len - p1); - memcpy(current->buf + p1, buf, n); - current->len += n; - - current->chars++; - if (current->pos >= pos) { - current->pos++; - } - return ret; - } - return 0; -} - -/** - * Captures up to 'n' characters starting at 'pos' for the cut buffer. - * - * This replaces any existing characters in the cut buffer. - */ -static void capture_chars(struct current *current, int pos, int n) -{ - if (pos >= 0 && (pos + n - 1) < current->chars) { - int p1 = utf8_index(current->buf, pos); - int nbytes = utf8_index(current->buf + p1, n); - - if (nbytes) { - free(current->capture); - /* Include space for the null terminator */ - current->capture = (char *)malloc(nbytes + 1); - memcpy(current->capture, current->buf + p1, nbytes); - current->capture[nbytes] = '\0'; - } - } -} - -/** - * Removes up to 'n' characters at cursor position 'pos'. - * - * Returns 0 if no chars were removed or non-zero otherwise. - */ -static int remove_chars(struct current *current, int pos, int n) -{ - int removed = 0; - - /* First save any chars which will be removed */ - capture_chars(current, pos, n); - - while (n-- && remove_char(current, pos)) { - removed++; - } - return removed; -} -/** - * Inserts the characters (string) 'chars' at the cursor position 'pos'. - * - * Returns 0 if no chars were inserted or non-zero otherwise. - */ -static int insert_chars(struct current *current, int pos, const char *chars) -{ - int inserted = 0; - - while (*chars) { - int ch; - int n = utf8_tounicode(chars, &ch); - if (insert_char(current, pos, ch) == 0) { - break; - } - inserted++; - pos++; - chars += n; - } - return inserted; -} - -#ifndef NO_COMPLETION -static linenoiseCompletionCallback *completionCallback = NULL; - -static void beep() { -#ifdef USE_TERMIOS - fprintf(stderr, "\x7"); - fflush(stderr); -#endif -} - -static void freeCompletions(linenoiseCompletions *lc) { - size_t i; - for (i = 0; i < lc->len; i++) - free(lc->cvec[i]); - free(lc->cvec); -} - -static int completeLine(struct current *current) { - linenoiseCompletions lc = { 0, NULL }; - int c = 0; - - completionCallback(current->buf,&lc); - if (lc.len == 0) { - beep(); - } else { - size_t stop = 0, i = 0; - - while(!stop) { - /* Show completion or original buffer */ - if (i < lc.len) { - struct current tmp = *current; - tmp.buf = lc.cvec[i]; - tmp.pos = tmp.len = strlen(tmp.buf); - tmp.chars = utf8_strlen(tmp.buf, tmp.len); - refreshLine(current->prompt, &tmp); - } else { - refreshLine(current->prompt, current); - } - - c = fd_read(current); - if (c == -1) { - break; - } - - switch(c) { - case '\t': /* tab */ - i = (i+1) % (lc.len+1); - if (i == lc.len) beep(); - break; - case 27: /* escape */ - /* Re-show original buffer */ - if (i < lc.len) { - refreshLine(current->prompt, current); - } - stop = 1; - break; - default: - /* Update buffer and return */ - if (i < lc.len) { - set_current(current,lc.cvec[i]); - } - stop = 1; - break; - } - } - } - - freeCompletions(&lc); - return c; /* Return last read character */ -} - -/* Register a callback function to be called for tab-completion. */ -void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { - completionCallback = fn; -} - -void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { - lc->cvec = (char **)realloc(lc->cvec,sizeof(char*)*(lc->len+1)); - lc->cvec[lc->len++] = strdup(str); -} - -#endif - -static int linenoiseEdit(struct current *current) { - int history_index = 0; - - /* The latest history entry is always our current buffer, that - * initially is just an empty string. */ - linenoiseHistoryAdd(""); - - set_current(current, ""); - refreshLine(current->prompt, current); - - while(1) { - int dir = -1; - int c = fd_read(current); - -#ifndef NO_COMPLETION - /* Only autocomplete when the callback is set. It returns < 0 when - * there was an error reading from fd. Otherwise it will return the - * character that should be handled next. */ - if (c == '\t' && current->pos == current->chars && completionCallback != NULL) { - c = completeLine(current); - /* Return on errors */ - if (c < 0) return current->len; - /* Read next character when 0 */ - if (c == 0) continue; - } -#endif - -process_char: - if (c == -1) return current->len; -#ifdef USE_TERMIOS - if (c == 27) { /* escape sequence */ - c = check_special(current->fd); - } -#endif - switch(c) { - case '\r': /* enter */ - history_len--; - free(history[history_len]); - return current->len; - case ctrl('C'): /* ctrl-c */ - errno = EAGAIN; - return -1; - case 127: /* backspace */ - case ctrl('H'): - if (remove_char(current, current->pos - 1) == 1) { - refreshLine(current->prompt, current); - } - break; - case ctrl('D'): /* ctrl-d */ - if (current->len == 0) { - /* Empty line, so EOF */ - history_len--; - free(history[history_len]); - return -1; - } - /* Otherwise fall through to delete char to right of cursor */ - case SPECIAL_DELETE: - if (remove_char(current, current->pos) == 1) { - refreshLine(current->prompt, current); - } - break; - case SPECIAL_INSERT: - /* Ignore. Expansion Hook. - * Future possibility: Toggle Insert/Overwrite Modes - */ - break; - case ctrl('W'): /* ctrl-w, delete word at left. save deleted chars */ - /* eat any spaces on the left */ - { - int pos = current->pos; - while (pos > 0 && get_char(current, pos - 1) == ' ') { - pos--; - } - - /* now eat any non-spaces on the left */ - while (pos > 0 && get_char(current, pos - 1) != ' ') { - pos--; - } - - if (remove_chars(current, pos, current->pos - pos)) { - refreshLine(current->prompt, current); - } - } - break; - case ctrl('R'): /* ctrl-r */ - { - /* Display the reverse-i-search prompt and process chars */ - char rbuf[50]; - char rprompt[80]; - int rchars = 0; - int rlen = 0; - int searchpos = history_len - 1; - - rbuf[0] = 0; - while (1) { - int n = 0; - const char *p = NULL; - int skipsame = 0; - int searchdir = -1; - - snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf); - refreshLine(rprompt, current); - c = fd_read(current); - if (c == ctrl('H') || c == 127) { - if (rchars) { - int p = utf8_index(rbuf, --rchars); - rbuf[p] = 0; - rlen = strlen(rbuf); - } - continue; - } -#ifdef USE_TERMIOS - if (c == 27) { - c = check_special(current->fd); - } -#endif - if (c == ctrl('P') || c == SPECIAL_UP) { - /* Search for the previous (earlier) match */ - if (searchpos > 0) { - searchpos--; - } - skipsame = 1; - } - else if (c == ctrl('N') || c == SPECIAL_DOWN) { - /* Search for the next (later) match */ - if (searchpos < history_len) { - searchpos++; - } - searchdir = 1; - skipsame = 1; - } - else if (c >= ' ') { - if (rlen >= (int)sizeof(rbuf) + 3) { - continue; - } - - n = utf8_getchars(rbuf + rlen, c); - rlen += n; - rchars++; - rbuf[rlen] = 0; - - /* Adding a new char resets the search location */ - searchpos = history_len - 1; - } - else { - /* Exit from incremental search mode */ - break; - } - - /* Now search through the history for a match */ - for (; searchpos >= 0 && searchpos < history_len; searchpos += searchdir) { - p = strstr(history[searchpos], rbuf); - if (p) { - /* Found a match */ - if (skipsame && strcmp(history[searchpos], current->buf) == 0) { - /* But it is identical, so skip it */ - continue; - } - /* Copy the matching line and set the cursor position */ - set_current(current,history[searchpos]); - current->pos = utf8_strlen(history[searchpos], p - history[searchpos]); - break; - } - } - if (!p && n) { - /* No match, so don't add it */ - rchars--; - rlen -= n; - rbuf[rlen] = 0; - } - } - if (c == ctrl('G') || c == ctrl('C')) { - /* ctrl-g terminates the search with no effect */ - set_current(current, ""); - c = 0; - } - else if (c == ctrl('J')) { - /* ctrl-j terminates the search leaving the buffer in place */ - c = 0; - } - /* Go process the char normally */ - refreshLine(current->prompt, current); - goto process_char; - } - break; - case ctrl('T'): /* ctrl-t */ - if (current->pos > 0 && current->pos <= current->chars) { - /* If cursor is at end, transpose the previous two chars */ - int fixer = (current->pos == current->chars); - c = get_char(current, current->pos - fixer); - remove_char(current, current->pos - fixer); - insert_char(current, current->pos - 1, c); - refreshLine(current->prompt, current); - } - break; - case ctrl('V'): /* ctrl-v */ - if (has_room(current, 3)) { - /* Insert the ^V first */ - if (insert_char(current, current->pos, c)) { - refreshLine(current->prompt, current); - /* Now wait for the next char. Can insert anything except \0 */ - c = fd_read(current); - - /* Remove the ^V first */ - remove_char(current, current->pos - 1); - if (c != -1) { - /* Insert the actual char */ - insert_char(current, current->pos, c); - } - refreshLine(current->prompt, current); - } - } - break; - case ctrl('B'): - case SPECIAL_LEFT: - if (current->pos > 0) { - current->pos--; - refreshLine(current->prompt, current); - } - break; - case ctrl('F'): - case SPECIAL_RIGHT: - if (current->pos < current->chars) { - current->pos++; - refreshLine(current->prompt, current); - } - break; - case SPECIAL_PAGE_UP: - dir = history_len - history_index - 1; /* move to start of history */ - goto history_navigation; - case SPECIAL_PAGE_DOWN: - dir = -history_index; /* move to 0 == end of history, i.e. current */ - goto history_navigation; - case ctrl('P'): - case SPECIAL_UP: - dir = 1; - goto history_navigation; - case ctrl('N'): - case SPECIAL_DOWN: -history_navigation: - if (history_len > 1) { - /* Update the current history entry before to - * overwrite it with tne next one. */ - free(history[history_len - 1 - history_index]); - history[history_len - 1 - history_index] = strdup(current->buf); - /* Show the new entry */ - history_index += dir; - if (history_index < 0) { - history_index = 0; - break; - } else if (history_index >= history_len) { - history_index = history_len - 1; - break; - } - set_current(current, history[history_len - 1 - history_index]); - refreshLine(current->prompt, current); - } - break; - case ctrl('A'): /* Ctrl+a, go to the start of the line */ - case SPECIAL_HOME: - current->pos = 0; - refreshLine(current->prompt, current); - break; - case ctrl('E'): /* ctrl+e, go to the end of the line */ - case SPECIAL_END: - current->pos = current->chars; - refreshLine(current->prompt, current); - break; - case ctrl('U'): /* Ctrl+u, delete to beginning of line, save deleted chars. */ - if (remove_chars(current, 0, current->pos)) { - refreshLine(current->prompt, current); - } - break; - case ctrl('K'): /* Ctrl+k, delete from current to end of line, save deleted chars. */ - if (remove_chars(current, current->pos, current->chars - current->pos)) { - refreshLine(current->prompt, current); - } - break; - case ctrl('Y'): /* Ctrl+y, insert saved chars at current position */ - if (current->capture && insert_chars(current, current->pos, current->capture)) { - refreshLine(current->prompt, current); - } - break; - case ctrl('L'): /* Ctrl+L, clear screen */ - clearScreen(current); - /* Force recalc of window size for serial terminals */ - current->cols = 0; - refreshLine(current->prompt, current); - break; - default: - /* Only tab is allowed without ^V */ - if (c == '\t' || c >= ' ') { - if (insert_char(current, current->pos, c) == 1) { - refreshLine(current->prompt, current); - } - } - break; - } - } - return current->len; -} - -int linenoiseColumns(void) -{ - struct current current; - enableRawMode (¤t); - getWindowSize (¤t); - disableRawMode (¤t); - return current.cols; -} - -char *linenoise(const char *prompt) -{ - int count; - struct current current; - char buf[LINENOISE_MAX_LINE]; - - if (enableRawMode(¤t) == -1) { - printf("%s", prompt); - fflush(stdout); - if (fgets(buf, sizeof(buf), stdin) == NULL) { - return NULL; - } - count = strlen(buf); - if (count && buf[count-1] == '\n') { - count--; - buf[count] = '\0'; - } - } - else - { - current.buf = buf; - current.bufmax = sizeof(buf); - current.len = 0; - current.chars = 0; - current.pos = 0; - current.prompt = prompt; - current.capture = NULL; - - count = linenoiseEdit(¤t); - - disableRawMode(¤t); - printf("\n"); - - free(current.capture); - if (count == -1) { - return NULL; - } - } - return strdup(buf); -} - -/* Using a circular buffer is smarter, but a bit more complex to handle. */ -int linenoiseHistoryAdd(const char *line) { - char *linecopy; - - if (history_max_len == 0) return 0; - if (history == NULL) { - history = (char **)malloc(sizeof(char*)*history_max_len); - if (history == NULL) return 0; - memset(history,0,(sizeof(char*)*history_max_len)); - } - - /* do not insert duplicate lines into history */ - if (history_len > 0 && strcmp(line, history[history_len - 1]) == 0) { - return 0; - } - - linecopy = strdup(line); - if (!linecopy) return 0; - if (history_len == history_max_len) { - free(history[0]); - memmove(history,history+1,sizeof(char*)*(history_max_len-1)); - history_len--; - } - history[history_len] = linecopy; - history_len++; - return 1; -} - -int linenoiseHistoryGetMaxLen(void) { - return history_max_len; -} - -int linenoiseHistorySetMaxLen(int len) { - char **newHistory; - - if (len < 1) return 0; - if (history) { - int tocopy = history_len; - - newHistory = (char **)malloc(sizeof(char*)*len); - if (newHistory == NULL) return 0; - - /* If we can't copy everything, free the elements we'll not use. */ - if (len < tocopy) { - int j; - - for (j = 0; j < tocopy-len; j++) free(history[j]); - tocopy = len; - } - memset(newHistory,0,sizeof(char*)*len); - memcpy(newHistory,history+(history_len-tocopy), sizeof(char*)*tocopy); - free(history); - history = newHistory; - } - history_max_len = len; - if (history_len > history_max_len) - history_len = history_max_len; - return 1; -} - -/* Save the history in the specified file. On success 0 is returned - * otherwise -1 is returned. */ -int linenoiseHistorySave(const char *filename) { - FILE *fp = fopen(filename,"w"); - int j; - - if (fp == NULL) return -1; - for (j = 0; j < history_len; j++) { - const char *str = history[j]; - /* Need to encode backslash, nl and cr */ - while (*str) { - if (*str == '\\') { - fputs("\\\\", fp); - } - else if (*str == '\n') { - fputs("\\n", fp); - } - else if (*str == '\r') { - fputs("\\r", fp); - } - else { - fputc(*str, fp); - } - str++; - } - fputc('\n', fp); - } - - fclose(fp); - return 0; -} - -/* Load the history from the specified file. If the file does not exist - * zero is returned and no operation is performed. - * - * If the file exists and the operation succeeded 0 is returned, otherwise - * on error -1 is returned. */ -int linenoiseHistoryLoad(const char *filename) { - FILE *fp = fopen(filename,"r"); - char buf[LINENOISE_MAX_LINE]; - - if (fp == NULL) return -1; - - while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { - char *src, *dest; - - /* Decode backslash escaped values */ - for (src = dest = buf; *src; src++) { - char ch = *src; - - if (ch == '\\') { - src++; - if (*src == 'n') { - ch = '\n'; - } - else if (*src == 'r') { - ch = '\r'; - } else { - ch = *src; - } - } - *dest++ = ch; - } - /* Remove trailing newline */ - if (dest != buf && (dest[-1] == '\n' || dest[-1] == '\r')) { - dest--; - } - *dest = 0; - - linenoiseHistoryAdd(buf); - } - fclose(fp); - return 0; -} - -/* Provide access to the history buffer. - * - * If 'len' is not NULL, the length is stored in *len. - */ -char **linenoiseHistory(int *len) { - if (len) { - *len = history_len; - } - return history; -} diff --git a/src/rt/linenoise/linenoise.h b/src/rt/linenoise/linenoise.h deleted file mode 100644 index 7ebf244ee80..00000000000 --- a/src/rt/linenoise/linenoise.h +++ /dev/null @@ -1,61 +0,0 @@ -/* linenoise.h -- guerrilla line editing library against the idea that a - * line editing lib needs to be 20,000 lines of C code. - * - * See linenoise.c for more information. - * - * ------------------------------------------------------------------------ - * - * Copyright (c) 2010, Salvatore Sanfilippo - * Copyright (c) 2010, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __LINENOISE_H -#define __LINENOISE_H - -#ifndef NO_COMPLETION -typedef struct linenoiseCompletions { - size_t len; - char **cvec; -} linenoiseCompletions; - -typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); -void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); -void linenoiseAddCompletion(linenoiseCompletions *, const char *); -#endif - -char *linenoise(const char *prompt); -int linenoiseHistoryAdd(const char *line); -int linenoiseHistorySetMaxLen(int len); -int linenoiseHistoryGetMaxLen(void); -int linenoiseHistorySave(const char *filename); -int linenoiseHistoryLoad(const char *filename); -void linenoiseHistoryFree(void); -char **linenoiseHistory(int *len); -int linenoiseColumns(void); - -#endif /* __LINENOISE_H */ diff --git a/src/rt/linenoise/utf8.c b/src/rt/linenoise/utf8.c deleted file mode 100644 index 26924b46c19..00000000000 --- a/src/rt/linenoise/utf8.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * UTF-8 utility functions - * - * (c) 2010 Steve Bennett - * - * See LICENCE for licence details. - */ - -#include -#include -#include -#include -#include "utf8.h" - -#ifdef USE_UTF8 -int utf8_fromunicode(char *p, unsigned short uc) -{ - if (uc <= 0x7f) { - *p = uc; - return 1; - } - else if (uc <= 0x7ff) { - *p++ = 0xc0 | ((uc & 0x7c0) >> 6); - *p = 0x80 | (uc & 0x3f); - return 2; - } - else { - *p++ = 0xe0 | ((uc & 0xf000) >> 12); - *p++ = 0x80 | ((uc & 0xfc0) >> 6); - *p = 0x80 | (uc & 0x3f); - return 3; - } -} - -int utf8_charlen(int c) -{ - if ((c & 0x80) == 0) { - return 1; - } - if ((c & 0xe0) == 0xc0) { - return 2; - } - if ((c & 0xf0) == 0xe0) { - return 3; - } - if ((c & 0xf8) == 0xf0) { - return 4; - } - /* Invalid sequence */ - return -1; -} - -int utf8_strlen(const char *str, int bytelen) -{ - int charlen = 0; - if (bytelen < 0) { - bytelen = strlen(str); - } - while (bytelen) { - int c; - int l = utf8_tounicode(str, &c); - charlen++; - str += l; - bytelen -= l; - } - return charlen; -} - -int utf8_index(const char *str, int index) -{ - const char *s = str; - while (index--) { - int c; - s += utf8_tounicode(s, &c); - } - return s - str; -} - -int utf8_charequal(const char *s1, const char *s2) -{ - int c1, c2; - - utf8_tounicode(s1, &c1); - utf8_tounicode(s2, &c2); - - return c1 == c2; -} - -int utf8_tounicode(const char *str, int *uc) -{ - unsigned const char *s = (unsigned const char *)str; - - if (s[0] < 0xc0) { - *uc = s[0]; - return 1; - } - if (s[0] < 0xe0) { - if ((s[1] & 0xc0) == 0x80) { - *uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80); - return 2; - } - } - else if (s[0] < 0xf0) { - if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) { - *uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80); - return 3; - } - } - - /* Invalid sequence, so just return the byte */ - *uc = *s; - return 1; -} - -#endif diff --git a/src/rt/linenoise/utf8.h b/src/rt/linenoise/utf8.h deleted file mode 100644 index 9537939876a..00000000000 --- a/src/rt/linenoise/utf8.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef UTF8_UTIL_H -#define UTF8_UTIL_H -/** - * UTF-8 utility functions - * - * (c) 2010 Steve Bennett - * - * See LICENCE for licence details. - */ - -#ifndef USE_UTF8 -#include - -/* No utf-8 support. 1 byte = 1 char */ -#define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B)) -#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1) -#define utf8_index(C, I) (I) -#define utf8_charlen(C) 1 - -#else -/** - * Converts the given unicode codepoint (0 - 0xffff) to utf-8 - * and stores the result at 'p'. - * - * Returns the number of utf-8 characters (1-3). - */ -int utf8_fromunicode(char *p, unsigned short uc); - -/** - * Returns the length of the utf-8 sequence starting with 'c'. - * - * Returns 1-4, or -1 if this is not a valid start byte. - * - * Note that charlen=4 is not supported by the rest of the API. - */ -int utf8_charlen(int c); - -/** - * Returns the number of characters in the utf-8 - * string of the given byte length. - * - * Any bytes which are not part of an valid utf-8 - * sequence are treated as individual characters. - * - * The string *must* be null terminated. - * - * Does not support unicode code points > \uffff - */ -int utf8_strlen(const char *str, int bytelen); - -/** - * Returns the byte index of the given character in the utf-8 string. - * - * The string *must* be null terminated. - * - * This will return the byte length of a utf-8 string - * if given the char length. - */ -int utf8_index(const char *str, int charindex); - -/** - * Returns the unicode codepoint corresponding to the - * utf-8 sequence 'str'. - * - * Stores the result in *uc and returns the number of bytes - * consumed. - * - * If 'str' is null terminated, then an invalid utf-8 sequence - * at the end of the string will be returned as individual bytes. - * - * If it is not null terminated, the length *must* be checked first. - * - * Does not support unicode code points > \uffff - */ -int utf8_tounicode(const char *str, int *uc); - -#endif - -#endif diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 13fc3f1aa81..9750e22e945 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -570,18 +570,6 @@ rust_drop_env_lock() { env_lock.unlock(); } -static lock_and_signal linenoise_lock; - -extern "C" CDECL void -rust_take_linenoise_lock() { - linenoise_lock.lock(); -} - -extern "C" CDECL void -rust_drop_linenoise_lock() { - linenoise_lock.unlock(); -} - static lock_and_signal dlerror_lock; extern "C" CDECL void diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 959d1fa85f3..fb9934c7601 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -133,13 +133,6 @@ tinfl_decompress_mem_to_heap rust_uv_ip4_port rust_uv_ip6_port rust_uv_tcp_getpeername -linenoise -linenoiseSetCompletionCallback -linenoiseAddCompletion -linenoiseHistoryAdd -linenoiseHistorySetMaxLen -linenoiseHistorySave -linenoiseHistoryLoad rust_raw_thread_start rust_raw_thread_join rust_raw_thread_delete @@ -187,8 +180,6 @@ rust_get_num_cpus rust_get_global_args_ptr rust_take_global_args_lock rust_drop_global_args_lock -rust_take_linenoise_lock -rust_drop_linenoise_lock rust_get_test_int rust_get_task rust_uv_get_loop_from_getaddrinfo_req diff --git a/src/test/auxiliary/issue_3882.rc b/src/test/auxiliary/issue_3882.rc deleted file mode 100644 index f6124088c9b..00000000000 --- a/src/test/auxiliary/issue_3882.rc +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[link(name = "linenoise", - vers = "0.1")]; -#[crate_type = "lib"]; - -pub mod issue_3882; diff --git a/src/test/auxiliary/issue_3882.rs b/src/test/auxiliary/issue_3882.rs deleted file mode 100644 index bb75758c741..00000000000 --- a/src/test/auxiliary/issue_3882.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -mod issue_3882 { - struct Completions { - len: libc::size_t, - } - - mod c { - extern { - fn linenoiseAddCompletion(lc: *mut Completions); - } - } -} diff --git a/src/test/run-pass/issue_3882.rs b/src/test/run-pass/issue_3882.rs deleted file mode 100644 index 202385681ce..00000000000 --- a/src/test/run-pass/issue_3882.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-test -// aux-build:issue_3882.rc -extern mod linenoise; -use linenoise::issue_3882::*; - -pub fn main() {} diff --git a/src/test/run-pass/rl-human-test.rs b/src/test/run-pass/rl-human-test.rs deleted file mode 100644 index 6a87a6502d2..00000000000 --- a/src/test/run-pass/rl-human-test.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast no compile flags for check-fast - -// we want this to be compiled to avoid bitrot, but the actual test -//has to be conducted by a human, i.e. someone (you?) compiling this -//file with a plain rustc invocation and running it and checking it -//works. - -// compile-flags: --cfg robot_mode - -extern mod extra; -use extra::rl; - -static HISTORY_FILE: &'static str = "rl-human-test-history.txt"; - -struct TestCompleter; - -impl rl::CompletionCb for TestCompleter { - fn complete(&self, line: ~str, suggest: &fn(~str)) { - if line.is_empty() { - suggest(~"empty") - } else { - for c in line.rev_iter().take(3) { - suggest(format!("{0}{1}{1}{1}", line, c)) - } - } - } -} - -fn main() { - // don't run this in robot mode, but still typecheck it. - if !cfg!(robot_mode) { - println("~~ Welcome to the rl test \"suite\". ~~"); - println!("Operations: - - restrict the history to 2 lines, - - set the tab-completion to suggest three copies of each of the last 3 letters (or 'empty'), - - add 'one' and 'two' to the history, - - save it to `{0}`, - - add 'three', - - prompt & save input (check the history & completion work and contains only 'two', 'three'), - - load from `{0}` - - prompt & save input (history should be 'one', 'two' again), - - prompt once more. - -The bool return values of each step are printed.", - HISTORY_FILE); - - println!("restricting history length: {}", rl::set_history_max_len(3)); - - unsafe { - rl::complete(@TestCompleter as @rl::CompletionCb); - } - - println!("adding 'one': {}", rl::add_history("one")); - println!("adding 'two': {}", rl::add_history("two")); - - println!("saving history: {}", rl::save_history(HISTORY_FILE)); - - println!("adding 'three': {}", rl::add_history("three")); - - match rl::read("> ") { - Some(s) => println!("saving input: {}", rl::add_history(s)), - None => return - } - println!("loading history: {}", rl::load_history(HISTORY_FILE)); - - match rl::read("> ") { - Some(s) => println!("saving input: {}", rl::add_history(s)), - None => return - } - - rl::read("> "); - } -}