From 4912428cb5c5b4bf9e575673e5480b5f269353db Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Wed, 3 Oct 2012 11:15:02 +1000 Subject: [PATCH 01/12] rusti: Add linenoise, wrap into core::rl and add rusti REPL tool Add Brian Leibig to AUTHORS.txt for REPL contributions --- .gitmodules | 3 + AUTHORS.txt | 1 + Makefile.in | 4 +- configure | 6 + mk/clean.mk | 8 +- mk/dist.mk | 2 + mk/install.mk | 2 + mk/platform.mk | 39 +++++- mk/pp.mk | 1 + mk/rt.mk | 4 +- mk/rustllvm.mk | 4 +- mk/target.mk | 16 +++ mk/tests.mk | 28 ++++ mk/tools.mk | 19 +++ src/README.txt | 4 + src/libcore/core.rc | 1 + src/libcore/rl.rs | 78 +++++++++++ src/linenoise | 1 + src/rusti/rusti.rc | 36 +++++ src/rusti/rusti.rs | 323 +++++++++++++++++++++++++++++++++++++++++++ src/rusti/wrapper.rs | 24 ++++ 21 files changed, 591 insertions(+), 13 deletions(-) create mode 100644 src/libcore/rl.rs create mode 160000 src/linenoise create mode 100644 src/rusti/rusti.rc create mode 100644 src/rusti/rusti.rs create mode 100644 src/rusti/wrapper.rs diff --git a/.gitmodules b/.gitmodules index d750f8eb5b7..f78aed6261a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "src/libuv"] path = src/libuv url = git://github.com/graydon/libuv.git +[submodule "src/linenoise"] + path = src/linenoise + url = git://github.com/antirez/linenoise.git diff --git a/AUTHORS.txt b/AUTHORS.txt index 08a3ec77807..cc9c1a460a2 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -23,6 +23,7 @@ Benjamin Peterson Brendan Eich Brian Anderson Brian J. Burg +Brian Leibig Chris Double Chris Peterson Damian Gryski diff --git a/Makefile.in b/Makefile.in index a7594261c29..26365917a3d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -74,6 +74,7 @@ endif CFG_RUSTC_FLAGS := $(RUSTFLAGS) CFG_GCCISH_CFLAGS := +CFG_GCCISH_CXXFLAGS := CFG_GCCISH_LINK_FLAGS := ifdef CFG_DISABLE_OPTIMIZE @@ -449,9 +450,10 @@ TSREQS := \ $(SREQ3_T_$(target)_H_$(CFG_HOST_TRIPLE))) FUZZ := $(HBIN2_H_$(CFG_HOST_TRIPLE))/fuzzer$(X) CARGO := $(HBIN2_H_$(CFG_HOST_TRIPLE))/cargo$(X) +RUSTI := $(HBIN2_H_$(CFG_HOST_TRIPLE))/rusti$(X) RUSTDOC := $(HBIN2_H_$(CFG_HOST_TRIPLE))/rustdoc$(X) -all: rustc $(GENERATED) docs $(FUZZ) $(CARGO) $(RUSTDOC) +all: rustc $(GENERATED) docs $(FUZZ) $(CARGO) $(RUSTDOC) $(RUSTI) endif diff --git a/configure b/configure index e67ea3af5f3..9c30bc2af14 100755 --- a/configure +++ b/configure @@ -508,6 +508,12 @@ do make_dir rustllvm/$t done +make_dir linenoise +for t in $CFG_TARGET_TRIPLES +do + make_dir linenoise/$t +done + make_dir rt for t in $CFG_TARGET_TRIPLES do diff --git a/mk/clean.mk b/mk/clean.mk index d36cbaf7d0b..9b606a845a5 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -23,7 +23,7 @@ clean: clean-misc $(CLEAN_STAGE_RULES) clean-misc: @$(call E, cleaning) - $(Q)find rustllvm rt $(CFG_HOST_TRIPLE)/test \ + $(Q)find linenoise rustllvm rt $(CFG_HOST_TRIPLE)/test \ -name '*.[odasS]' -o \ -name '*.so' -o \ -name '*.dylib' -o \ @@ -31,11 +31,12 @@ clean-misc: -name '*.def' -o \ -name '*.bc' \ | xargs rm -f - $(Q)find rustllvm rt $(CFG_HOST_TRIPLE)\ + $(Q)find linenoise rustllvm rt $(CFG_HOST_TRIPLE)\ -name '*.dSYM' \ | xargs rm -Rf $(Q)rm -f $(RUNTIME_OBJS) $(RUNTIME_DEF) $(Q)rm -f $(RUSTLLVM_LIB_OBJS) $(RUSTLLVM_OBJS_OBJS) $(RUSTLLVM_DEF) + $(Q)rm -f $(LINENOISE_OBJS) $(Q)rm -Rf $(DOCS) $(Q)rm -Rf $(GENERATED) $(Q)rm -f tmp/*.log tmp/*.rc tmp/*.rs @@ -55,6 +56,7 @@ clean$(1)_H_$(2): $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustc$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/fuzzer$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/cargo$(X) + $(Q)rm -f $$(HBIN$(1)_H_$(2))/rusti$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/serializer$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustdoc$(X) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUNTIME) @@ -81,6 +83,7 @@ clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/fuzzer$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/cargo$(X) + $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rusti$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/serializer$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustdoc$(X) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUNTIME) @@ -95,6 +98,7 @@ clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a + $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/liblinenoise.a endef $(foreach host, $(CFG_TARGET_TRIPLES), \ diff --git a/mk/dist.mk b/mk/dist.mk index dd6a7aba4d6..af4c6d8741b 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -22,6 +22,7 @@ PKG_FILES := \ $(addprefix $(S)src/, \ README.txt \ cargo \ + rusti \ rustc \ compiletest \ etc \ @@ -29,6 +30,7 @@ PKG_FILES := \ libcore \ libsyntax \ libstd \ + linenoise \ rt \ rustdoc \ rustllvm \ diff --git a/mk/install.mk b/mk/install.mk index 3a2dbceaaad..8bc691e813e 100644 --- a/mk/install.mk +++ b/mk/install.mk @@ -72,6 +72,7 @@ install-host: $(SREQ$(ISTAGE)_T_$(CFG_HOST_TRIPLE)_H_$(CFG_HOST_TRIPLE)) $(Q)mkdir -p $(PREFIX_ROOT)/share/man/man1 $(Q)$(call INSTALL,$(HB2),$(PHB),rustc$(X)) $(Q)$(call INSTALL,$(HB2),$(PHB),cargo$(X)) + $(Q)$(call INSTALL,$(HB2),$(PHB),rusti$(X)) $(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc$(X)) $(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUNTIME)) $(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(CORELIB_GLOB)) @@ -91,6 +92,7 @@ HOST_LIB_FROM_HL_GLOB = \ uninstall: $(Q)rm -f $(PHB)/rustc$(X) $(Q)rm -f $(PHB)/cargo$(X) + $(Q)rm -f $(PHB)/rusti$(X) $(Q)rm -f $(PHB)/rustdoc$(X) $(Q)rm -f $(PHL)/$(CFG_RUSTLLVM) $(Q)rm -f $(PHL)/$(CFG_RUNTIME) diff --git a/mk/platform.mk b/mk/platform.mk index 67eaa1b878b..010ddecb131 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -212,20 +212,33 @@ ifeq ($(CFG_C_COMPILER),clang) ifeq ($(origin CPP),default) CPP=clang -E endif - CFG_GCCISH_CFLAGS += -Wall -Werror -fno-rtti -g + CFG_GCCISH_CFLAGS += -Wall -Werror -g + CFG_GCCISH_CXXFLAGS += -fno-rtti CFG_GCCISH_LINK_FLAGS += -g # These flags will cause the compiler to produce a .d file # next to the .o file that lists header deps. CFG_DEPEND_FLAGS = -MMD -MP -MT $(1) -MF $(1:%.o=%.d) define CFG_MAKE_CC - CFG_COMPILE_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + CFG_COMPILE_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CC) \ + $$(CFG_GCCISH_CFLAGS) $$(CFG_CLANG_CFLAGS) \ + $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_CLANG_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_DEPEND_FLAGS) \ + -c -o $$(1) $$(2) + CFG_LINK_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CC) \ + $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ + $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ + $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ + $$(call CFG_INSTALL_NAME,$$(4)) + CFG_COMPILE_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ $$(CFG_GCCISH_CFLAGS) $$(CFG_CLANG_CFLAGS) \ + $$(CFG_GCCISH_CXXFLAGS) \ $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ $$(CFG_CLANG_CFLAGS_$$(HOST_$(1))) \ $$(CFG_DEPEND_FLAGS) \ -c -o $$(1) $$(2) - CFG_LINK_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + CFG_LINK_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ @@ -245,21 +258,35 @@ ifeq ($(CFG_C_COMPILER),gcc) ifeq ($(origin CPP),default) CPP=gcc -E endif - CFG_GCCISH_CFLAGS += -Wall -Werror -fno-rtti -g + CFG_GCCISH_CFLAGS += -Wall -Werror -g + CFG_GCCISH_CXXFLAGS += -fno-rtti CFG_GCCISH_LINK_FLAGS += -g # These flags will cause the compiler to produce a .d file # next to the .o file that lists header deps. CFG_DEPEND_FLAGS = -MMD -MP -MT $(1) -MF $(1:%.o=%.d) define CFG_MAKE_CC - CFG_COMPILE_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + CFG_COMPILE_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CC) \ + $$(CFG_GCCISH_CFLAGS) \ + $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_GCC_CFLAGS) \ + $$(CFG_GCC_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_DEPEND_FLAGS) \ + -c -o $$(1) $$(2) + CFG_LINK_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CC) \ + $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ + $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ + $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ + $$(call CFG_INSTALL_NAME,$$(4)) + CFG_COMPILE_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ $$(CFG_GCCISH_CFLAGS) \ + $$(CFG_GCCISH_CXXFLAGS) \ $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ $$(CFG_GCC_CFLAGS) \ $$(CFG_GCC_CFLAGS_$$(HOST_$(1))) \ $$(CFG_DEPEND_FLAGS) \ -c -o $$(1) $$(2) - CFG_LINK_C_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + CFG_LINK_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ diff --git a/mk/pp.mk b/mk/pp.mk index dd956216bbe..bf2a64f9b62 100644 --- a/mk/pp.mk +++ b/mk/pp.mk @@ -9,6 +9,7 @@ else $(S)src/test/*/*/*.rs) \ $(wildcard $(S)src/fuzzer/*.rs) \ $(wildcard $(S)src/cargo/*.rs) + $(wildcard $(S)src/rusti/*.rs) PP_INPUTS_FILTERED = $(shell echo $(PP_INPUTS) | xargs grep -L \ "no-reformat\|xfail-pretty\|xfail-test") diff --git a/mk/rt.mk b/mk/rt.mk index 2304bcbf9d2..573aad99cd0 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -117,7 +117,7 @@ RUNTIME_LIBS_$(1) := $$(LIBUV_LIB_$(1)) rt/$(1)/%.o: rt/%.cpp $$(MKFILE_DEPS) @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_C_$(1), $$@, $$(RUNTIME_INCS_$(1)) \ + $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(RUNTIME_INCS_$(1)) \ $$(SNAP_DEFINES)) $$< rt/$(1)/%.o: rt/%.S $$(MKFILE_DEPS) \ @@ -133,7 +133,7 @@ rt/$(1)/$(CFG_RUNTIME): $$(RUNTIME_OBJS_$(1)) $$(MKFILE_DEPS) \ $$(RUNTIME_DEF_$(1)) \ $$(RUNTIME_LIBS_$(1)) @$$(call E, link: $$@) - $$(Q)$$(call CFG_LINK_C_$(1),$$@, $$(RUNTIME_OBJS_$(1)) \ + $$(Q)$$(call CFG_LINK_CXX_$(1),$$@, $$(RUNTIME_OBJS_$(1)) \ $$(CFG_GCCISH_POST_LIB_FLAGS) $$(RUNTIME_LIBS_$(1)) \ $$(CFG_LIBUV_LINK_FLAGS),$$(RUNTIME_DEF_$(1)),$$(CFG_RUNTIME)) diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 622f7d4fa09..c56220af05e 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -25,14 +25,14 @@ ALL_OBJ_FILES += $$(RUSTLLVM_OBJS_OBJS_$(1)) rustllvm/$(1)/$(CFG_RUSTLLVM): $$(RUSTLLVM_OBJS_OBJS_$(1)) \ $$(MKFILE_DEPS) $$(RUSTLLVM_DEF_$(1)) @$$(call E, link: $$@) - $$(Q)$$(call CFG_LINK_C_$(1),$$@,$$(RUSTLLVM_OBJS_OBJS_$(1)) \ + $$(Q)$$(call CFG_LINK_CXX_$(1),$$@,$$(RUSTLLVM_OBJS_OBJS_$(1)) \ $$(CFG_GCCISH_PRE_LIB_FLAGS) $$(LLVM_LIBS_$(1)) \ $$(CFG_GCCISH_POST_LIB_FLAGS) \ $$(LLVM_LDFLAGS_$(1)),$$(RUSTLLVM_DEF_$(1)),$$(CFG_RUSTLLVM)) rustllvm/$(1)/%.o: rustllvm/%.cpp $$(MKFILE_DEPS) $$(LLVM_CONFIG_$(1)) @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_C_$(1), $$@, $$(LLVM_CXXFLAGS_$(1)) $$(RUSTLLVM_INCS_$(1))) $$< + $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(LLVM_CXXFLAGS_$(1)) $$(RUSTLLVM_INCS_$(1))) $$< endef # Instantiate template for all stages diff --git a/mk/target.mk b/mk/target.mk index 967191ab745..51c89597ca6 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -12,6 +12,17 @@ USE_SNAPSHOT_RUNTIME=0 USE_SNAPSHOT_CORELIB=0 USE_SNAPSHOT_STDLIB=0 +LINENOISE_OBJS_$(2) := linenoise/$(2)/linenoise.o +ALL_OBJ_FILES += $$(LINENOISE_OBJS_$(2)) + +linenoise/$(2)/linenoise.o: linenoise/linenoise.c $$(MKFILE_DEPS) + @$$(call E, compile: $$@) + $$(Q)$$(call CFG_COMPILE_C_$(2), $$@,) $$< + +linenoise/$(2)/liblinenoise.a: $$(LINENOISE_OBJS_$(2)) + @$$(call E, link: $$@) + $$(Q)ar rcs $$@ $$< + define TARGET_STAGE_N $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a: \ @@ -19,6 +30,11 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a: \ @$$(call E, cp: $$@) $$(Q)cp $$< $$@ +$$(TLIB$(1)_T_$(2)_H_$(3))/liblinenoise.a: \ + linenoise/$(2)/liblinenoise.a + @$$(call E, cp: $$@) + $$(Q)cp $$< $$@ + $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_RUSTLLVM): \ rustllvm/$(2)/$$(CFG_RUSTLLVM) @$$(call E, cp: $$@) diff --git a/mk/tests.mk b/mk/tests.mk index d8457a4f497..7c6b9795c51 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -229,6 +229,7 @@ check-stage$(1)-T-$(2)-H-$(3): \ check-stage$(1)-T-$(2)-H-$(3)-bench \ check-stage$(1)-T-$(2)-H-$(3)-pretty \ check-stage$(1)-T-$(2)-H-$(3)-rustdoc \ + check-stage$(1)-T-$(2)-H-$(3)-rusti \ check-stage$(1)-T-$(2)-H-$(3)-cargo \ check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial \ check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-ffi \ @@ -289,6 +290,9 @@ check-stage$(1)-T-$(2)-H-$(3)-pretty-pretty: \ check-stage$(1)-T-$(2)-H-$(3)-rustdoc: \ check-stage$(1)-T-$(2)-H-$(3)-rustdoc-dummy +check-stage$(1)-T-$(2)-H-$(3)-rusti: \ + check-stage$(1)-T-$(2)-H-$(3)-rusti-dummy + check-stage$(1)-T-$(2)-H-$(3)-cargo: \ check-stage$(1)-T-$(2)-H-$(3)-cargo-dummy @@ -371,6 +375,23 @@ check-stage$(1)-T-$(2)-H-$(3)-rustdoc-dummy: \ $$(Q)$$(call CFG_RUN_TEST,$$<,$(2),$(3)) $$(TESTARGS) \ --logfile tmp/check-stage$(1)-T-$(2)-H-$(3)-rustdoc.log +# Rules for the rusti test runner + +$(3)/test/rustitest.stage$(1)-$(2)$$(X): \ + $$(RUSTI_CRATE) $$(RUSTI_INPUTS) \ + $$(TSREQ$(1)_T_$(2)_H_$(3)) \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_CORELIB) \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_STDLIB) \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC) + @$$(call E, compile_and_link: $$@) + $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test + +check-stage$(1)-T-$(2)-H-$(3)-rusti-dummy: \ + $(3)/test/rustitest.stage$(1)-$(2)$$(X) + @$$(call E, run: $$<) + $$(Q)$$(call CFG_RUN_TEST,$$<,$(2),$(3)) $$(TESTARGS) \ + --logfile tmp/check-stage$(1)-T-$(2)-H-$(3)-rusti.log + # Rules for the cargo test runner $(3)/test/cargotest.stage$(1)-$(2)$$(X): \ @@ -756,6 +777,9 @@ check-stage$(1)-H-$(2)-pretty-pretty: \ check-stage$(1)-H-$(2)-rustdoc: \ $$(foreach target,$$(CFG_TARGET_TRIPLES), \ check-stage$(1)-T-$$(target)-H-$(2)-rustdoc) +check-stage$(1)-H-$(2)-rusti: \ + $$(foreach target,$$(CFG_TARGET_TRIPLES), \ + check-stage$(1)-T-$$(target)-H-$(2)-rusti) check-stage$(1)-H-$(2)-cargo: \ $$(foreach target,$$(CFG_TARGET_TRIPLES), \ check-stage$(1)-T-$$(target)-H-$(2)-cargo) @@ -846,6 +870,9 @@ check-stage$(1)-H-all-pretty-pretty: \ check-stage$(1)-H-all-rustdoc: \ $$(foreach target,$$(CFG_TARGET_TRIPLES), \ check-stage$(1)-H-$$(target)-rustdoc) +check-stage$(1)-H-all-rusti: \ + $$(foreach target,$$(CFG_TARGET_TRIPLES), \ + check-stage$(1)-H-$$(target)-rusti) check-stage$(1)-H-all-cargo: \ $$(foreach target,$$(CFG_TARGET_TRIPLES), \ check-stage$(1)-H-$$(target)-cargo) @@ -880,6 +907,7 @@ check-stage$(1)-pretty-rfail: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-pretty-rfail check-stage$(1)-pretty-bench: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-pretty-bench check-stage$(1)-pretty-pretty: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-pretty-pretty check-stage$(1)-rustdoc: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-rustdoc +check-stage$(1)-rusti: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-rusti check-stage$(1)-cargo: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-cargo check-stage$(1)-doc-tutorial: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial check-stage$(1)-doc-tutorial-ffi: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial-ffi diff --git a/mk/tools.mk b/mk/tools.mk index 6b7810de73b..a17305081db 100644 --- a/mk/tools.mk +++ b/mk/tools.mk @@ -12,6 +12,10 @@ COMPILETEST_INPUTS := $(wildcard $(S)src/compiletest/*rs) CARGO_CRATE := $(S)src/cargo/cargo.rc CARGO_INPUTS := $(wildcard $(S)src/cargo/*rs) +# Rusti, the JIT REPL +RUSTI_CRATE := $(S)src/rusti/rusti.rc +RUSTI_INPUTS := $(wildcard $(S)src/rusti/*rs) + # Rustdoc, the documentation tool RUSTDOC_CRATE := $(S)src/rustdoc/rustdoc.rc RUSTDOC_INPUTS := $(wildcard $(S)src/rustdoc/*.rs) @@ -62,12 +66,27 @@ $$(TBIN$(1)_T_$(4)_H_$(3))/cargo$$(X): \ @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< +$$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X): \ + $$(RUSTI_CRATE) $$(RUSTI_INPUTS) \ + $$(TSREQ$(1)_T_$(4)_H_$(3)) \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_CORELIB) \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_STDLIB) \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTC) + @$$(call E, compile_and_link: $$@) + $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< + $$(HBIN$(2)_H_$(4))/cargo$$(X): \ $$(TBIN$(1)_T_$(4)_H_$(3))/cargo$$(X) \ $$(HSREQ$(2)_H_$(4)) @$$(call E, cp: $$@) $$(Q)cp $$< $$@ +$$(HBIN$(2)_H_$(4))/rusti$$(X): \ + $$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X) \ + $$(HSREQ$(2)_H_$(4)) + @$$(call E, cp: $$@) + $$(Q)cp $$< $$@ + $$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc$$(X): \ $$(RUSTDOC_CRATE) $$(RUSTDOC_INPUTS) \ $$(TSREQ$(1)_T_$(4)_H_$(3)) \ diff --git a/src/README.txt b/src/README.txt index 5e0d9bd0e84..e435d51463e 100644 --- a/src/README.txt +++ b/src/README.txt @@ -30,6 +30,8 @@ compiletest/ The test runner cargo/ The package manager +rusti/ The JIT REPL + rustdoc/ The Rust API documentation tool llvm/ The LLVM submodule @@ -38,6 +40,8 @@ libuv/ The libuv submodule rustllvm/ LLVM support code +linenoise\ Minimalistic libreadline alternative + fuzzer/ A collection of fuzz testers etc/ Scripts, editor support, misc diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 94e6decc4ca..742385241b5 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -152,6 +152,7 @@ pub mod option_iter { pub mod inst; } pub mod result; +pub mod rl; pub mod to_str; pub mod to_bytes; pub mod from_str; diff --git a/src/libcore/rl.rs b/src/libcore/rl.rs new file mode 100644 index 00000000000..5230b0596ca --- /dev/null +++ b/src/libcore/rl.rs @@ -0,0 +1,78 @@ +use libc::{c_char, c_int}; + +#[link_args = "-Llinenoise"] +#[link_name = "linenoise"] +#[abi = "cdecl"] +extern mod linenoise { + #[legacy_exports]; + fn linenoise(prompt: *c_char) -> *c_char; + fn linenoiseHistoryAdd(line: *c_char) -> c_int; + fn linenoiseHistorySetMaxLen(len: c_int) -> c_int; + fn linenoiseHistorySave(file: *c_char) -> c_int; + fn linenoiseHistoryLoad(file: *c_char) -> c_int; + fn linenoiseSetCompletionCallback(callback: *u8); + fn linenoiseAddCompletion(completions: *(), line: *c_char); + fn linenoiseClearScreen(); +} + +/// Add a line to history +pub fn add_history(line: ~str) -> bool { + do str::as_c_str(line) |buf| { + linenoise::linenoiseHistoryAdd(buf) == 1 as c_int + } +} + +/// Set the maximum amount of lines stored +pub fn set_history_max_len(len: int) -> bool { + linenoise::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int +} + +/// Save line history to a file +pub fn save_history(file: ~str) -> bool { + do str::as_c_str(file) |buf| { + linenoise::linenoiseHistorySave(buf) == 1 as c_int + } +} + +/// Load line history from a file +pub fn load_history(file: ~str) -> bool { + do str::as_c_str(file) |buf| { + linenoise::linenoiseHistoryLoad(buf) == 1 as c_int + } +} + +/// Print out a prompt and then wait for input and return it +pub fn read(prompt: ~str) -> Option<~str> { + do str::as_c_str(prompt) |buf| unsafe { + let line = linenoise::linenoise(buf); + + if line.is_null() { None } + else { Some(str::raw::from_c_str(line)) } + } +} + +/// Clear the screen +pub fn clear() { + linenoise::linenoiseClearScreen(); +} + +pub type CompletionCb = fn~(~str, fn(~str)); + +fn complete_key(_v: @CompletionCb) {} + +/// Bind to the main completion callback +pub fn complete(cb: CompletionCb) unsafe { + task::local_data::local_data_set(complete_key, @(move cb)); + + extern fn callback(line: *c_char, completions: *()) unsafe { + let cb: CompletionCb = copy *task::local_data::local_data_get(complete_key).get(); + + do cb(str::raw::from_c_str(line)) |suggestion| { + do str::as_c_str(suggestion) |buf| { + linenoise::linenoiseAddCompletion(completions, buf); + } + } + } + + linenoise::linenoiseSetCompletionCallback(callback); +} diff --git a/src/linenoise b/src/linenoise new file mode 160000 index 00000000000..8c9b481281b --- /dev/null +++ b/src/linenoise @@ -0,0 +1 @@ +Subproject commit 8c9b481281ba401f6baf45bc9ca9fc940b59405f diff --git a/src/rusti/rusti.rc b/src/rusti/rusti.rc new file mode 100644 index 00000000000..612c57add81 --- /dev/null +++ b/src/rusti/rusti.rc @@ -0,0 +1,36 @@ +// rusti - REPL using the JIT backend + +#[link(name = "rusti", + vers = "0.4", + uuid = "7fb5bf52-7d45-4fee-8325-5ad3311149fc", + url = "https://github.com/mozilla/rust/tree/master/src/rusti")]; + +#[crate_type = "bin"]; + +#[no_core]; + +#[allow(vecs_implicitly_copyable, + non_implicitly_copyable_typarams)]; +#[allow(non_camel_case_types)]; +#[allow(deprecated_mode)]; +#[allow(deprecated_pattern)]; + +extern mod core(vers = "0.4"); +extern mod std(vers = "0.4"); +extern mod rustc(vers = "0.4"); +extern mod syntax(vers = "0.4"); + +use core::*; +use io::{ReaderUtil, WriterUtil}; +use std::c_vec; +use rustc::back; +use rustc::driver::{driver, session}; +use rustc::front; +use rustc::lib::llvm::llvm; +use rustc::metadata::{creader, filesearch}; +use rustc::middle::{freevars, kind, lint, trans, ty, typeck}; +use rustc::middle; +use syntax::{ast, ast_util, codemap, diagnostic, fold, parse, print, visit}; +use syntax::ast_util::*; +use parse::token; +use print::{pp, pprust}; diff --git a/src/rusti/rusti.rs b/src/rusti/rusti.rs new file mode 100644 index 00000000000..d12172ac69d --- /dev/null +++ b/src/rusti/rusti.rs @@ -0,0 +1,323 @@ +/** + * A structure shared across REPL instances for storing history + * such as statements and view items. I wish the AST was sendable. + */ +struct Repl { + prompt: ~str, + binary: ~str, + running: bool, + view_items: ~str, + stmts: ~str +} + +/// A utility function that hands off a pretty printer to a callback. +fn with_pp(intr: @token::ident_interner, + cb: fn(pprust::ps, io::Writer)) -> ~str { + do io::with_str_writer |writer| { + let pp = pprust::rust_printer(writer, intr); + + cb(pp, writer); + pp::eof(pp.s); + } +} + +/** + * The AST (or the rest of rustc) are not sendable yet, + * so recorded things are printed to strings. A terrible hack that + * needs changes to rustc in order to be outed. This is unfortunately + * going to cause the REPL to regress in parser performance, + * because it has to parse the statements and view_items on each + * input. + */ +fn record(repl: Repl, blk: @ast::blk, intr: @token::ident_interner) -> Repl { + let view_items = if blk.node.view_items.len() > 0 { + let new_view_items = do with_pp(intr) |pp, writer| { + for blk.node.view_items.each |view_item| { + pprust::print_view_item(pp, *view_item); + writer.write_line(~""); + } + }; + + debug!("new view items %s", new_view_items); + + repl.view_items + "\n" + new_view_items + } else { repl.view_items }; + let stmts = if blk.node.stmts.len() > 0 { + let new_stmts = do with_pp(intr) |pp, writer| { + for blk.node.stmts.each |stmt| { + match stmt.node { + ast::stmt_decl(*) => { + pprust::print_stmt(pp, **stmt); + writer.write_line(~""); + } + ast::stmt_expr(expr, _) | ast::stmt_semi(expr, _) => { + match expr.node { + ast::expr_assign(*) | + ast::expr_assign_op(*) | + ast::expr_swap(*) => { + pprust::print_stmt(pp, **stmt); + writer.write_line(~""); + } + _ => {} + } + } + } + } + }; + + debug!("new stmts %s", new_stmts); + + repl.stmts + "\n" + new_stmts + } else { repl.stmts }; + + Repl{ + view_items: view_items, + stmts: stmts, + .. repl + } +} + +/// Run an input string in a Repl, returning the new Repl. +fn run(repl: Repl, input: ~str) -> Repl { + let options: @session::options = @{ + crate_type: session::unknown_crate, + binary: repl.binary, + .. *session::basic_options() + }; + + debug!("building driver input"); + let head = include_str!("wrapper.rs"); + let foot = fmt!("%s\nfn main() {\n%s\n\nprint({\n%s\n})\n}", + repl.view_items, repl.stmts, input); + let wrapped = driver::str_input(head + foot); + + debug!("inputting %s", head + foot); + + debug!("building a driver session"); + let sess = driver::build_session(options, diagnostic::emit); + + debug!("building driver configuration"); + let cfg = driver::build_configuration(sess, + repl.binary, + wrapped); + + debug!("parsing"); + let mut crate = driver::parse_input(sess, cfg, wrapped); + let mut opt = None; + + for crate.node.module.items.each |item| { + match item.node { + ast::item_fn(_, _, _, blk) => { + if item.ident == sess.ident_of(~"main") { + opt = blk.node.expr; + } + } + _ => {} + } + } + + let blk = match opt.get().node { + ast::expr_call(_, exprs, _) => { + match exprs[0].node { + ast::expr_block(blk) => @blk, + _ => fail + } + } + _ => fail + }; + + debug!("configuration"); + crate = front::config::strip_unconfigured_items(crate); + + debug!("maybe building test harness"); + crate = front::test::modify_for_testing(sess, crate); + + debug!("expansion"); + crate = syntax::ext::expand::expand_crate(sess.parse_sess, + sess.opts.cfg, + crate); + + debug!("intrinsic injection"); + crate = front::intrinsic_inject::inject_intrinsic(sess, crate); + + debug!("core injection"); + crate = front::core_inject::maybe_inject_libcore_ref(sess, crate); + + debug!("building lint settings table"); + lint::build_settings_crate(sess, crate); + + debug!("ast indexing"); + let ast_map = syntax::ast_map::map_crate(sess.diagnostic(), *crate); + + debug!("external crate/lib resolution"); + creader::read_crates(sess.diagnostic(), *crate, sess.cstore, + sess.filesearch, + session::sess_os_to_meta_os(sess.targ_cfg.os), + sess.opts.static, sess.parse_sess.interner); + + debug!("language item collection"); + let lang_items = middle::lang_items::collect_language_items(crate, sess); + + debug!("resolution"); + let {def_map: def_map, + exp_map2: exp_map2, + trait_map: trait_map} = middle::resolve::resolve_crate(sess, + lang_items, + crate); + + debug!("freevar finding"); + let freevars = freevars::annotate_freevars(def_map, crate); + + debug!("region_resolution"); + let region_map = middle::region::resolve_crate(sess, def_map, crate); + + debug!("region paramaterization inference"); + let rp_set = middle::region::determine_rp_in_crate(sess, ast_map, + def_map, crate); + + debug!("typechecking"); + let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars, + region_map, rp_set, move lang_items, crate); + let (method_map, vtable_map) = typeck::check_crate(ty_cx, trait_map, + crate); + + debug!("const marking"); + middle::const_eval::process_crate(crate, def_map, ty_cx); + + debug!("const checking"); + middle::check_const::check_crate(sess, crate, ast_map, def_map, + method_map, ty_cx); + + debug!("privacy checking"); + middle::privacy::check_crate(ty_cx, &method_map, crate); + + debug!("loop checking"); + middle::check_loop::check_crate(ty_cx, crate); + + debug!("alt checking"); + middle::check_alt::check_crate(ty_cx, crate); + + debug!("liveness checking"); + let last_use_map = middle::liveness::check_crate(ty_cx, + method_map, crate); + + debug!("borrow checking"); + let (root_map, mutbl_map) = middle::borrowck::check_crate(ty_cx, + method_map, + last_use_map, + crate); + + debug!("kind checking"); + kind::check_crate(ty_cx, method_map, last_use_map, crate); + + debug!("lint checking"); + lint::check_crate(ty_cx, crate); + + let maps = {mutbl_map: mutbl_map, + root_map: root_map, + last_use_map: last_use_map, + method_map: method_map, + vtable_map: vtable_map}; + + debug!("translation"); + let (llmod, _) = trans::base::trans_crate(sess, crate, ty_cx, + ~path::from_str(""), + exp_map2, maps); + let pm = llvm::LLVMCreatePassManager(); + + debug!("executing jit"); + back::link::jit::exec(sess, pm, llmod, 0, false); + llvm::LLVMDisposePassManager(pm); + + debug!("recording input into repl history"); + record(repl, blk, sess.parse_sess.interner) +} + +/// Run a command, e.g. :clear, :exit, etc. +fn run_cmd(repl: &mut Repl, _in: io::Reader, _out: io::Writer, + cmd: ~str, _args: ~[~str]) { + match cmd { + ~"exit" => repl.running = false, + ~"clear" => { + repl.view_items = ~""; + repl.stmts = ~""; + + rl::clear(); + } + ~"help" => { + io::println(~":clear - clear the screen\n" + + ~":exit - exit from the repl\n" + + ~":help - show this message"); + } + _ => io::println(~"unknown cmd: " + cmd) + } +} + +fn main() { + let args = os::args(); + let in = io::stdin(); + let out = io::stdout(); + let mut repl = Repl { + prompt: ~"rusti> ", + binary: args[0], + running: true, + view_items: ~"", + stmts: ~"" + }; + + do rl::complete |line, suggest| { + if line.starts_with(":") { + suggest(~":clear"); + suggest(~":exit"); + suggest(~":help"); + } + } + + while repl.running { + let result = rl::read(repl.prompt); + + if result.is_none() { + break; + } + + let line = result.get(); + + if line.is_empty() { + io::println(~"()"); + + loop; + } + + rl::add_history(line); + + if line.starts_with(~":") { + let full = line.substr(1, line.len() - 1); + let split = full.split_char(' '); + let len = split.len(); + + if len > 0 { + let cmd = split[0]; + + if !cmd.is_empty() { + let args = if len > 1 { + do vec::view(split, 1, len - 1).map |arg| { + *arg + } + } else { ~[] }; + + run_cmd(&mut repl, in, out, cmd, args); + + loop; + } + } + } + + let result = do task::try |copy repl| { + run(copy repl, line) + }; + + if result.is_ok() { + repl = result.get(); + } + } +} diff --git a/src/rusti/wrapper.rs b/src/rusti/wrapper.rs new file mode 100644 index 00000000000..0e6510c88df --- /dev/null +++ b/src/rusti/wrapper.rs @@ -0,0 +1,24 @@ +#[legacy_modes]; +#[legacy_exports]; + +#[allow(ctypes)]; +#[allow(deprecated_mode)]; +#[allow(deprecated_pattern)]; +#[allow(heap_memory)]; +#[allow(implicit_copies)]; +#[allow(managed_heap_memory)]; +#[allow(non_camel_case_types)]; +#[allow(non_implicitly_copyable_typarams)]; +#[allow(owned_heap_memory)]; +#[allow(path_statement)]; +#[allow(structural_records)]; +#[allow(unrecognized_lint)]; +#[allow(unused_imports)]; +#[allow(vecs_implicitly_copyable)]; +#[allow(while_true)]; + +extern mod std; + +fn print(result: T) { + io::println(fmt!("%?", result)); +} From f93b1ff6f63b93e032c28c8ead17633aab3e4f98 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sat, 27 Oct 2012 18:06:14 +1000 Subject: [PATCH 02/12] Add linenoise to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bfcdf15fd2f..14bc613cb56 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ version.texi config.mk /rt/ /rustllvm/ +/linenoise /test/ /build/ /inst/ From c97c8131a558595c0a19a283b99ef1ba4638f853 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sat, 27 Oct 2012 18:52:47 +1000 Subject: [PATCH 03/12] rusti: Add current working directory to search path --- src/rusti/rusti.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rusti/rusti.rs b/src/rusti/rusti.rs index d12172ac69d..b1c2d0778fa 100644 --- a/src/rusti/rusti.rs +++ b/src/rusti/rusti.rs @@ -82,6 +82,7 @@ fn run(repl: Repl, input: ~str) -> Repl { let options: @session::options = @{ crate_type: session::unknown_crate, binary: repl.binary, + addl_lib_search_paths: ~[os::getcwd()], .. *session::basic_options() }; From 9ed8ce33646e0253fda91471fe528d1800bdf70c Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sat, 27 Oct 2012 21:16:26 +1000 Subject: [PATCH 04/12] Correct makefiles to build linenoise correctly --- Makefile.in | 4 +++- mk/linenoise.mk | 23 +++++++++++++++++++++++ mk/target.mk | 11 ----------- 3 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 mk/linenoise.mk diff --git a/Makefile.in b/Makefile.in index 26365917a3d..93cf3a82fca 100644 --- a/Makefile.in +++ b/Makefile.in @@ -337,7 +337,8 @@ HSREQ$(1)_H_$(3) = \ TSREQ$(1)_T_$(2)_H_$(3) = \ $$(HSREQ$(1)_H_$(3)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_RUNTIME) \ - $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a + $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a \ + $$(TLIB$(1)_T_$(2)_H_$(3))/liblinenoise.a # Prerequisites for complete stageN targets SREQ$(1)_T_$(2)_H_$(3) = \ @@ -491,6 +492,7 @@ include $(CFG_SRC_DIR)mk/host.mk include $(CFG_SRC_DIR)mk/stage0.mk include $(CFG_SRC_DIR)mk/rt.mk include $(CFG_SRC_DIR)mk/rustllvm.mk +include $(CFG_SRC_DIR)mk/linenoise.mk include $(CFG_SRC_DIR)mk/tools.mk include $(CFG_SRC_DIR)mk/docs.mk include $(CFG_SRC_DIR)mk/llvm.mk diff --git a/mk/linenoise.mk b/mk/linenoise.mk new file mode 100644 index 00000000000..19a94a429b3 --- /dev/null +++ b/mk/linenoise.mk @@ -0,0 +1,23 @@ +###################################################################### +# linenoise - minimalistic readline alternative used by the REPL +###################################################################### + +define DEF_LINENOISE_TARGETS + +LINENOISE_CS_$(1) := $$(addprefix linenoise/, linenoise.c) +LINENOISE_OBJS_$(1) := $(LINENOISE_CS_$(1):linenoise/%.c=linenoise/$(1)/%.o) + +ALL_OBJ_FILES += $$(LINENOISE_OBJS_$(1)) + +linenoise/$(1)/liblinenoise.a: $$(LINENOISE_OBJS_$(1)) + @$$(call E, link: $$@) + $$(Q)ar rcs $$@ $$< + +linenoise/$(1)/%.o: linenoise/%.c $$(MKFILE_DEPS) + @$$(call E, compile: $$@) + $$(Q)$$(call CFG_COMPILE_C_$(1), $$@,) $$< +endef + +# Instantiate template for all stages +$(foreach target,$(CFG_TARGET_TRIPLES), \ + $(eval $(call DEF_LINENOISE_TARGETS,$(target)))) diff --git a/mk/target.mk b/mk/target.mk index 51c89597ca6..231bf56ba83 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -12,17 +12,6 @@ USE_SNAPSHOT_RUNTIME=0 USE_SNAPSHOT_CORELIB=0 USE_SNAPSHOT_STDLIB=0 -LINENOISE_OBJS_$(2) := linenoise/$(2)/linenoise.o -ALL_OBJ_FILES += $$(LINENOISE_OBJS_$(2)) - -linenoise/$(2)/linenoise.o: linenoise/linenoise.c $$(MKFILE_DEPS) - @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_C_$(2), $$@,) $$< - -linenoise/$(2)/liblinenoise.a: $$(LINENOISE_OBJS_$(2)) - @$$(call E, link: $$@) - $$(Q)ar rcs $$@ $$< - define TARGET_STAGE_N $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a: \ From 1afa29986fe62cf817e886412dfb6d42b2d5f7e5 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sat, 27 Oct 2012 21:41:41 +1000 Subject: [PATCH 05/12] rusti: Correct formatting --- src/libcore/rl.rs | 52 +++++++++++++++++++++++----------------------- src/rusti/rusti.rs | 4 ++-- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/libcore/rl.rs b/src/libcore/rl.rs index 5230b0596ca..96833dd4e44 100644 --- a/src/libcore/rl.rs +++ b/src/libcore/rl.rs @@ -17,43 +17,43 @@ extern mod linenoise { /// Add a line to history pub fn add_history(line: ~str) -> bool { - do str::as_c_str(line) |buf| { - linenoise::linenoiseHistoryAdd(buf) == 1 as c_int - } + do str::as_c_str(line) |buf| { + linenoise::linenoiseHistoryAdd(buf) == 1 as c_int + } } /// Set the maximum amount of lines stored pub fn set_history_max_len(len: int) -> bool { - linenoise::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int + linenoise::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int } /// Save line history to a file pub fn save_history(file: ~str) -> bool { - do str::as_c_str(file) |buf| { - linenoise::linenoiseHistorySave(buf) == 1 as c_int - } + do str::as_c_str(file) |buf| { + linenoise::linenoiseHistorySave(buf) == 1 as c_int + } } /// Load line history from a file pub fn load_history(file: ~str) -> bool { - do str::as_c_str(file) |buf| { - linenoise::linenoiseHistoryLoad(buf) == 1 as c_int - } + do str::as_c_str(file) |buf| { + linenoise::linenoiseHistoryLoad(buf) == 1 as c_int + } } /// Print out a prompt and then wait for input and return it pub fn read(prompt: ~str) -> Option<~str> { - do str::as_c_str(prompt) |buf| unsafe { - let line = linenoise::linenoise(buf); + do str::as_c_str(prompt) |buf| unsafe { + let line = linenoise::linenoise(buf); - if line.is_null() { None } - else { Some(str::raw::from_c_str(line)) } - } + if line.is_null() { None } + else { Some(str::raw::from_c_str(line)) } + } } /// Clear the screen pub fn clear() { - linenoise::linenoiseClearScreen(); + linenoise::linenoiseClearScreen(); } pub type CompletionCb = fn~(~str, fn(~str)); @@ -62,17 +62,17 @@ fn complete_key(_v: @CompletionCb) {} /// Bind to the main completion callback pub fn complete(cb: CompletionCb) unsafe { - task::local_data::local_data_set(complete_key, @(move cb)); + task::local_data::local_data_set(complete_key, @(move cb)); - extern fn callback(line: *c_char, completions: *()) unsafe { - let cb: CompletionCb = copy *task::local_data::local_data_get(complete_key).get(); + extern fn callback(line: *c_char, completions: *()) unsafe { + let cb = copy *task::local_data::local_data_get(complete_key).get(); - do cb(str::raw::from_c_str(line)) |suggestion| { - do str::as_c_str(suggestion) |buf| { - linenoise::linenoiseAddCompletion(completions, buf); - } - } - } + do cb(str::raw::from_c_str(line)) |suggestion| { + do str::as_c_str(suggestion) |buf| { + linenoise::linenoiseAddCompletion(completions, buf); + } + } + } - linenoise::linenoiseSetCompletionCallback(callback); + linenoise::linenoiseSetCompletionCallback(callback); } diff --git a/src/rusti/rusti.rs b/src/rusti/rusti.rs index b1c2d0778fa..2fa4684b322 100644 --- a/src/rusti/rusti.rs +++ b/src/rusti/rusti.rs @@ -246,7 +246,7 @@ fn run_cmd(repl: &mut Repl, _in: io::Reader, _out: io::Writer, rl::clear(); } ~"help" => { - io::println(~":clear - clear the screen\n" + + io::println(~":clear - clear the screen\n" + ~":exit - exit from the repl\n" + ~":help - show this message"); } @@ -294,7 +294,7 @@ fn main() { if line.starts_with(~":") { let full = line.substr(1, line.len() - 1); let split = full.split_char(' '); - let len = split.len(); + let len = split.len(); if len > 0 { let cmd = split[0]; From ea996556b93f3f322145fedb5a46fe97e8d0379a Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Tue, 30 Oct 2012 11:08:36 +1000 Subject: [PATCH 06/12] rusti: Remove linenoise module, add to rt, remove core::rl --- .gitmodules | 3 - Makefile.in | 7 +- configure | 8 +- mk/clean.mk | 8 +- mk/dist.mk | 1 - mk/linenoise.mk | 23 -- mk/platform.mk | 42 +-- mk/pp.mk | 2 +- mk/rt.mk | 17 +- mk/target.mk | 5 - mk/tests.mk | 7 +- mk/tools.mk | 38 +-- src/README.txt | 4 +- src/libcore/core.rc | 1 - src/linenoise | 1 - src/rt/linenoise/linenoise.c | 626 +++++++++++++++++++++++++++++++++++ src/rt/linenoise/linenoise.h | 56 ++++ src/rt/rustrt.def.in | 8 + src/{libcore => rusti}/rl.rs | 23 +- src/rusti/rusti.rc | 6 +- src/rusti/wrapper.rs | 4 +- 21 files changed, 765 insertions(+), 125 deletions(-) delete mode 100644 mk/linenoise.mk delete mode 160000 src/linenoise create mode 100644 src/rt/linenoise/linenoise.c create mode 100644 src/rt/linenoise/linenoise.h rename src/{libcore => rusti}/rl.rs (75%) diff --git a/.gitmodules b/.gitmodules index f78aed6261a..d750f8eb5b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "src/libuv"] path = src/libuv url = git://github.com/graydon/libuv.git -[submodule "src/linenoise"] - path = src/linenoise - url = git://github.com/antirez/linenoise.git diff --git a/Makefile.in b/Makefile.in index 93cf3a82fca..ffed7915d2b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -74,7 +74,6 @@ endif CFG_RUSTC_FLAGS := $(RUSTFLAGS) CFG_GCCISH_CFLAGS := -CFG_GCCISH_CXXFLAGS := CFG_GCCISH_LINK_FLAGS := ifdef CFG_DISABLE_OPTIMIZE @@ -337,8 +336,7 @@ HSREQ$(1)_H_$(3) = \ TSREQ$(1)_T_$(2)_H_$(3) = \ $$(HSREQ$(1)_H_$(3)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_RUNTIME) \ - $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a \ - $$(TLIB$(1)_T_$(2)_H_$(3))/liblinenoise.a + $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a # Prerequisites for complete stageN targets SREQ$(1)_T_$(2)_H_$(3) = \ @@ -451,8 +449,8 @@ TSREQS := \ $(SREQ3_T_$(target)_H_$(CFG_HOST_TRIPLE))) FUZZ := $(HBIN2_H_$(CFG_HOST_TRIPLE))/fuzzer$(X) CARGO := $(HBIN2_H_$(CFG_HOST_TRIPLE))/cargo$(X) -RUSTI := $(HBIN2_H_$(CFG_HOST_TRIPLE))/rusti$(X) RUSTDOC := $(HBIN2_H_$(CFG_HOST_TRIPLE))/rustdoc$(X) +RUSTI := $(HBIN2_H_$(CFG_HOST_TRIPLE))/rusti$(X) all: rustc $(GENERATED) docs $(FUZZ) $(CARGO) $(RUSTDOC) $(RUSTI) @@ -492,7 +490,6 @@ include $(CFG_SRC_DIR)mk/host.mk include $(CFG_SRC_DIR)mk/stage0.mk include $(CFG_SRC_DIR)mk/rt.mk include $(CFG_SRC_DIR)mk/rustllvm.mk -include $(CFG_SRC_DIR)mk/linenoise.mk include $(CFG_SRC_DIR)mk/tools.mk include $(CFG_SRC_DIR)mk/docs.mk include $(CFG_SRC_DIR)mk/llvm.mk diff --git a/configure b/configure index 9c30bc2af14..50eb9deeff3 100755 --- a/configure +++ b/configure @@ -508,18 +508,12 @@ do make_dir rustllvm/$t done -make_dir linenoise -for t in $CFG_TARGET_TRIPLES -do - make_dir linenoise/$t -done - make_dir rt for t in $CFG_TARGET_TRIPLES do make_dir rt/$t for i in \ - isaac bigint sync test arch/i386 arch/x86_64 \ + isaac linenoise bigint sync test arch/i386 arch/x86_64 \ libuv libuv/src/ares libuv/src/eio libuv/src/ev do make_dir rt/$t/$i diff --git a/mk/clean.mk b/mk/clean.mk index 9b606a845a5..d36cbaf7d0b 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -23,7 +23,7 @@ clean: clean-misc $(CLEAN_STAGE_RULES) clean-misc: @$(call E, cleaning) - $(Q)find linenoise rustllvm rt $(CFG_HOST_TRIPLE)/test \ + $(Q)find rustllvm rt $(CFG_HOST_TRIPLE)/test \ -name '*.[odasS]' -o \ -name '*.so' -o \ -name '*.dylib' -o \ @@ -31,12 +31,11 @@ clean-misc: -name '*.def' -o \ -name '*.bc' \ | xargs rm -f - $(Q)find linenoise rustllvm rt $(CFG_HOST_TRIPLE)\ + $(Q)find rustllvm rt $(CFG_HOST_TRIPLE)\ -name '*.dSYM' \ | xargs rm -Rf $(Q)rm -f $(RUNTIME_OBJS) $(RUNTIME_DEF) $(Q)rm -f $(RUSTLLVM_LIB_OBJS) $(RUSTLLVM_OBJS_OBJS) $(RUSTLLVM_DEF) - $(Q)rm -f $(LINENOISE_OBJS) $(Q)rm -Rf $(DOCS) $(Q)rm -Rf $(GENERATED) $(Q)rm -f tmp/*.log tmp/*.rc tmp/*.rs @@ -56,7 +55,6 @@ clean$(1)_H_$(2): $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustc$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/fuzzer$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/cargo$(X) - $(Q)rm -f $$(HBIN$(1)_H_$(2))/rusti$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/serializer$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustdoc$(X) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUNTIME) @@ -83,7 +81,6 @@ clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/fuzzer$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/cargo$(X) - $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rusti$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/serializer$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustdoc$(X) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUNTIME) @@ -98,7 +95,6 @@ clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a - $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/liblinenoise.a endef $(foreach host, $(CFG_TARGET_TRIPLES), \ diff --git a/mk/dist.mk b/mk/dist.mk index af4c6d8741b..0bdf764c153 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -30,7 +30,6 @@ PKG_FILES := \ libcore \ libsyntax \ libstd \ - linenoise \ rt \ rustdoc \ rustllvm \ diff --git a/mk/linenoise.mk b/mk/linenoise.mk deleted file mode 100644 index 19a94a429b3..00000000000 --- a/mk/linenoise.mk +++ /dev/null @@ -1,23 +0,0 @@ -###################################################################### -# linenoise - minimalistic readline alternative used by the REPL -###################################################################### - -define DEF_LINENOISE_TARGETS - -LINENOISE_CS_$(1) := $$(addprefix linenoise/, linenoise.c) -LINENOISE_OBJS_$(1) := $(LINENOISE_CS_$(1):linenoise/%.c=linenoise/$(1)/%.o) - -ALL_OBJ_FILES += $$(LINENOISE_OBJS_$(1)) - -linenoise/$(1)/liblinenoise.a: $$(LINENOISE_OBJS_$(1)) - @$$(call E, link: $$@) - $$(Q)ar rcs $$@ $$< - -linenoise/$(1)/%.o: linenoise/%.c $$(MKFILE_DEPS) - @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_C_$(1), $$@,) $$< -endef - -# Instantiate template for all stages -$(foreach target,$(CFG_TARGET_TRIPLES), \ - $(eval $(call DEF_LINENOISE_TARGETS,$(target)))) diff --git a/mk/platform.mk b/mk/platform.mk index 010ddecb131..ba0d0765524 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -103,7 +103,7 @@ endif # Hack: not sure how to test if a file exists in make other than this OS_SUPP = $(patsubst %,--suppressions=%,\ - $(wildcard $(CFG_SRC_DIR)src/etc/$(CFG_OSTYPE).supp*)) + $(wildcard $(CFG_SRC_DIR)src/etc/$(CFG_OSTYPE).supp*)) ifneq ($(findstring mingw,$(CFG_OSTYPE)),) CFG_WINDOWSY := 1 @@ -231,18 +231,18 @@ ifeq ($(CFG_C_COMPILER),clang) $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ $$(call CFG_INSTALL_NAME,$$(4)) - CFG_COMPILE_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ - $$(CFG_GCCISH_CFLAGS) $$(CFG_CLANG_CFLAGS) \ + CFG_COMPILE_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + $$(CFG_GCCISH_CFLAGS) $$(CFG_CLANG_CFLAGS) \ $$(CFG_GCCISH_CXXFLAGS) \ - $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ - $$(CFG_CLANG_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_CLANG_CFLAGS_$$(HOST_$(1))) \ $$(CFG_DEPEND_FLAGS) \ - -c -o $$(1) $$(2) - CFG_LINK_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ - $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ - $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ - $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ - $$(call CFG_INSTALL_NAME,$$(4)) + -c -o $$(1) $$(2) + CFG_LINK_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ + $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ + $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ + $$(call CFG_INSTALL_NAME,$$(4)) endef $(foreach target,$(CFG_TARGET_TRIPLES), \ @@ -278,18 +278,18 @@ ifeq ($(CFG_C_COMPILER),gcc) $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ $$(call CFG_INSTALL_NAME,$$(4)) - CFG_COMPILE_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ - $$(CFG_GCCISH_CFLAGS) \ + CFG_COMPILE_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + $$(CFG_GCCISH_CFLAGS) \ $$(CFG_GCCISH_CXXFLAGS) \ - $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ - $$(CFG_GCC_CFLAGS) \ - $$(CFG_GCC_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_GCCISH_CFLAGS_$$(HOST_$(1))) \ + $$(CFG_GCC_CFLAGS) \ + $$(CFG_GCC_CFLAGS_$$(HOST_$(1))) \ $$(CFG_DEPEND_FLAGS) \ -c -o $$(1) $$(2) - CFG_LINK_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ - $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ - $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ - $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ + CFG_LINK_CXX_$(1) = $$(CFG_GCCISH_CROSS)$$(CXX) \ + $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ + $$(CFG_GCCISH_LINK_FLAGS_$$(HOST_$(1))) \ + $$(CFG_GCCISH_DEF_FLAG)$$(3) $$(2) \ $$(call CFG_INSTALL_NAME,$$(4)) endef @@ -312,4 +312,4 @@ define CFG_MAKE_ASSEMBLER endef $(foreach target,$(CFG_TARGET_TRIPLES),\ - $(eval $(call CFG_MAKE_ASSEMBLER,$(target)))) + $(eval $(call CFG_MAKE_ASSEMBLER,$(target)))) \ No newline at end of file diff --git a/mk/pp.mk b/mk/pp.mk index bf2a64f9b62..34ca47463fd 100644 --- a/mk/pp.mk +++ b/mk/pp.mk @@ -8,7 +8,7 @@ else $(wildcard $(S)src/test/*/*.rs \ $(S)src/test/*/*/*.rs) \ $(wildcard $(S)src/fuzzer/*.rs) \ - $(wildcard $(S)src/cargo/*.rs) + $(wildcard $(S)src/cargo/*.rs) \ $(wildcard $(S)src/rusti/*.rs) PP_INPUTS_FILTERED = $(shell echo $(PP_INPUTS) | xargs grep -L \ diff --git a/mk/rt.mk b/mk/rt.mk index 573aad99cd0..17f9b8b2f7b 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -42,7 +42,7 @@ define DEF_RUNTIME_TARGETS # Runtime (C++) library variables ###################################################################### -RUNTIME_CS_$(1) := \ +RUNTIME_CXXS_$(1) := \ rt/sync/timer.cpp \ rt/sync/lock_and_signal.cpp \ rt/sync/rust_thread.cpp \ @@ -78,6 +78,8 @@ RUNTIME_CS_$(1) := \ rt/arch/$$(HOST_$(1))/context.cpp \ rt/arch/$$(HOST_$(1))/gpr.cpp +RUNTIME_CS_$(1) := rt/linenoise/linenoise.c + RUNTIME_S_$(1) := rt/arch/$$(HOST_$(1))/_context.S \ rt/arch/$$(HOST_$(1))/ccall.S \ rt/arch/$$(HOST_$(1))/record_sp.S @@ -104,9 +106,11 @@ endif RUNTIME_DEF_$(1) := rt/rustrt$$(CFG_DEF_SUFFIX) RUNTIME_INCS_$(1) := -I $$(S)src/rt -I $$(S)src/rt/isaac -I $$(S)src/rt/uthash \ - -I $$(S)src/rt/arch/$$(HOST_$(1)) \ - -I $$(S)src/libuv/include -RUNTIME_OBJS_$(1) := $$(RUNTIME_CS_$(1):rt/%.cpp=rt/$(1)/%.o) \ + -I $$(S)src/rt/arch/$$(HOST_$(1)) \ + -I $$(S)src/rt/linenoise \ + -I $$(S)src/libuv/include +RUNTIME_OBJS_$(1) := $$(RUNTIME_CXXS_$(1):rt/%.cpp=rt/$(1)/%.o) \ + $$(RUNTIME_CS_$(1):rt/%.c=rt/$(1)/%.o) \ $$(RUNTIME_S_$(1):rt/%.S=rt/$(1)/%.o) ALL_OBJ_FILES += $$(RUNTIME_OBJS_$(1)) @@ -120,6 +124,11 @@ rt/$(1)/%.o: rt/%.cpp $$(MKFILE_DEPS) $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(RUNTIME_INCS_$(1)) \ $$(SNAP_DEFINES)) $$< +rt/$(1)/%.o: rt/%.c $$(MKFILE_DEPS) + @$$(call E, compile: $$@) + $$(Q)$$(call CFG_COMPILE_C_$(1), $$@, $$(RUNTIME_INCS_$(1)) \ + $$(SNAP_DEFINES)) $$< + rt/$(1)/%.o: rt/%.S $$(MKFILE_DEPS) \ $$(LLVM_CONFIG_$$(CFG_HOST_TRIPLE)) @$$(call E, compile: $$@) diff --git a/mk/target.mk b/mk/target.mk index 231bf56ba83..967191ab745 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -19,11 +19,6 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a: \ @$$(call E, cp: $$@) $$(Q)cp $$< $$@ -$$(TLIB$(1)_T_$(2)_H_$(3))/liblinenoise.a: \ - linenoise/$(2)/liblinenoise.a - @$$(call E, cp: $$@) - $$(Q)cp $$< $$@ - $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_RUSTLLVM): \ rustllvm/$(2)/$$(CFG_RUSTLLVM) @$$(call E, cp: $$@) diff --git a/mk/tests.mk b/mk/tests.mk index 7c6b9795c51..bfb2b63c09a 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -229,7 +229,7 @@ check-stage$(1)-T-$(2)-H-$(3): \ check-stage$(1)-T-$(2)-H-$(3)-bench \ check-stage$(1)-T-$(2)-H-$(3)-pretty \ check-stage$(1)-T-$(2)-H-$(3)-rustdoc \ - check-stage$(1)-T-$(2)-H-$(3)-rusti \ + check-stage$(1)-T-$(2)-H-$(3)-rusti \ check-stage$(1)-T-$(2)-H-$(3)-cargo \ check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial \ check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-ffi \ @@ -392,6 +392,7 @@ check-stage$(1)-T-$(2)-H-$(3)-rusti-dummy: \ $$(Q)$$(call CFG_RUN_TEST,$$<,$(2),$(3)) $$(TESTARGS) \ --logfile tmp/check-stage$(1)-T-$(2)-H-$(3)-rusti.log + # Rules for the cargo test runner $(3)/test/cargotest.stage$(1)-$(2)$$(X): \ @@ -870,9 +871,6 @@ check-stage$(1)-H-all-pretty-pretty: \ check-stage$(1)-H-all-rustdoc: \ $$(foreach target,$$(CFG_TARGET_TRIPLES), \ check-stage$(1)-H-$$(target)-rustdoc) -check-stage$(1)-H-all-rusti: \ - $$(foreach target,$$(CFG_TARGET_TRIPLES), \ - check-stage$(1)-H-$$(target)-rusti) check-stage$(1)-H-all-cargo: \ $$(foreach target,$$(CFG_TARGET_TRIPLES), \ check-stage$(1)-H-$$(target)-cargo) @@ -907,7 +905,6 @@ check-stage$(1)-pretty-rfail: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-pretty-rfail check-stage$(1)-pretty-bench: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-pretty-bench check-stage$(1)-pretty-pretty: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-pretty-pretty check-stage$(1)-rustdoc: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-rustdoc -check-stage$(1)-rusti: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-rusti check-stage$(1)-cargo: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-cargo check-stage$(1)-doc-tutorial: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial check-stage$(1)-doc-tutorial-ffi: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial-ffi diff --git a/mk/tools.mk b/mk/tools.mk index a17305081db..cdfad867126 100644 --- a/mk/tools.mk +++ b/mk/tools.mk @@ -12,14 +12,14 @@ COMPILETEST_INPUTS := $(wildcard $(S)src/compiletest/*rs) CARGO_CRATE := $(S)src/cargo/cargo.rc CARGO_INPUTS := $(wildcard $(S)src/cargo/*rs) -# Rusti, the JIT REPL -RUSTI_CRATE := $(S)src/rusti/rusti.rc -RUSTI_INPUTS := $(wildcard $(S)src/rusti/*rs) - # Rustdoc, the documentation tool RUSTDOC_CRATE := $(S)src/rustdoc/rustdoc.rc RUSTDOC_INPUTS := $(wildcard $(S)src/rustdoc/*.rs) +# Rusti, the JIT REPL +RUSTI_CRATE := $(S)src/rusti/rusti.rc +RUSTI_INPUTS := $(wildcard $(S)src/rusti/*.rs) + # FIXME: These are only built for the host arch. Eventually we'll # have tools that need to built for other targets. define TOOLS_STAGE_N @@ -66,27 +66,12 @@ $$(TBIN$(1)_T_$(4)_H_$(3))/cargo$$(X): \ @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< -$$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X): \ - $$(RUSTI_CRATE) $$(RUSTI_INPUTS) \ - $$(TSREQ$(1)_T_$(4)_H_$(3)) \ - $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_CORELIB) \ - $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_STDLIB) \ - $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTC) - @$$(call E, compile_and_link: $$@) - $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< - $$(HBIN$(2)_H_$(4))/cargo$$(X): \ $$(TBIN$(1)_T_$(4)_H_$(3))/cargo$$(X) \ $$(HSREQ$(2)_H_$(4)) @$$(call E, cp: $$@) $$(Q)cp $$< $$@ -$$(HBIN$(2)_H_$(4))/rusti$$(X): \ - $$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X) \ - $$(HSREQ$(2)_H_$(4)) - @$$(call E, cp: $$@) - $$(Q)cp $$< $$@ - $$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc$$(X): \ $$(RUSTDOC_CRATE) $$(RUSTDOC_INPUTS) \ $$(TSREQ$(1)_T_$(4)_H_$(3)) \ @@ -102,6 +87,21 @@ $$(HBIN$(2)_H_$(4))/rustdoc$$(X): \ @$$(call E, cp: $$@) $$(Q)cp $$< $$@ +$$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X): \ + $$(RUSTI_CRATE) $$(RUSTI_INPUTS) \ + $$(TSREQ$(1)_T_$(4)_H_$(3)) \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_CORELIB) \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_STDLIB) \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTC) + @$$(call E, compile_and_link: $$@) + $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< + +$$(HBIN$(2)_H_$(4))/rusti$$(X): \ + $$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X) \ + $$(HSREQ$(2)_H_$(4)) + @$$(call E, cp: $$@) + $$(Q)cp $$< $$@ + endef $(foreach host,$(CFG_TARGET_TRIPLES), \ diff --git a/src/README.txt b/src/README.txt index e435d51463e..d32751cf576 100644 --- a/src/README.txt +++ b/src/README.txt @@ -30,7 +30,7 @@ compiletest/ The test runner cargo/ The package manager -rusti/ The JIT REPL +rusti/ The JIT REPL rustdoc/ The Rust API documentation tool @@ -40,8 +40,6 @@ libuv/ The libuv submodule rustllvm/ LLVM support code -linenoise\ Minimalistic libreadline alternative - fuzzer/ A collection of fuzz testers etc/ Scripts, editor support, misc diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 742385241b5..94e6decc4ca 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -152,7 +152,6 @@ pub mod option_iter { pub mod inst; } pub mod result; -pub mod rl; pub mod to_str; pub mod to_bytes; pub mod from_str; diff --git a/src/linenoise b/src/linenoise deleted file mode 160000 index 8c9b481281b..00000000000 --- a/src/linenoise +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8c9b481281ba401f6baf45bc9ca9fc940b59405f diff --git a/src/rt/linenoise/linenoise.c b/src/rt/linenoise/linenoise.c new file mode 100644 index 00000000000..4632f7de818 --- /dev/null +++ b/src/rt/linenoise/linenoise.c @@ -0,0 +1,626 @@ +/* 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/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 + * + * 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 + * + * Todo list: + * - Switch to gets() if $TERM is something we can't support. + * - Filter bogus Ctrl+ combinations. + * - Win32 support + * + * Bloat: + * - Completion? + * - History search like Ctrl+r in readline? + * + * List of escape sequences used by this program, we do everything just + * with three sequences. In order to be so cheap we may have some + * flickering effect with some slow terminal, but the lesser sequences + * the more compatible. + * + * CHA (Cursor Horizontal Absolute) + * Sequence: ESC [ n G + * Effect: moves cursor to column n + * + * EL (Erase Line) + * Sequence: ESC [ n K + * Effect: if n is 0 or missing, clear from cursor to end of line + * Effect: if n is 1, clear from beginning of line to cursor + * Effect: if n is 2, clear entire line + * + * CUF (CUrsor Forward) + * Sequence: ESC [ n C + * Effect: moves cursor forward of n chars + * + * The following are used to clear the screen: ESC [ H ESC [ 2 J + * This is actually composed of two sequences: + * + * cursorhome + * Sequence: ESC [ H + * Effect: moves the cursor to upper left corner + * + * ED2 (Clear entire screen) + * Sequence: ESC [ 2 J + * Effect: clear the whole screen + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linenoise.h" + +#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 +#define LINENOISE_MAX_LINE 4096 +static char *unsupported_term[] = {"dumb","cons25",NULL}; +static linenoiseCompletionCallback *completionCallback = NULL; + +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 int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; +static int history_len = 0; +char **history = NULL; + +static void linenoiseAtExit(void); +int linenoiseHistoryAdd(const char *line); + +static int isUnsupportedTerm(void) { + char *term = getenv("TERM"); + int j; + + if (term == NULL) return 0; + for (j = 0; unsupported_term[j]; j++) + if (!strcasecmp(term,unsupported_term[j])) return 1; + return 0; +} + +static void freeHistory(void) { + if (history) { + int j; + + for (j = 0; j < history_len; j++) + free(history[j]); + free(history); + } +} + +static int enableRawMode(int fd) { + struct termios raw; + + if (!isatty(STDIN_FILENO)) goto fatal; + if (!atexit_registered) { + atexit(linenoiseAtExit); + atexit_registered = 1; + } + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + + 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(fd,TCSAFLUSH,&raw) < 0) goto fatal; + rawmode = 1; + return 0; + +fatal: + errno = ENOTTY; + return -1; +} + +static void disableRawMode(int fd) { + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) + rawmode = 0; +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +static void linenoiseAtExit(void) { + disableRawMode(STDIN_FILENO); + freeHistory(); +} + +static int getColumns(void) { + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80; + return ws.ws_col; +} + +static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) { + char seq[64]; + size_t plen = strlen(prompt); + + while((plen+pos) >= cols) { + buf++; + len--; + pos--; + } + while (plen+len > cols) { + len--; + } + + /* Cursor to left edge */ + snprintf(seq,64,"\x1b[0G"); + if (write(fd,seq,strlen(seq)) == -1) return; + /* Write the prompt and the current buffer content */ + if (write(fd,prompt,strlen(prompt)) == -1) return; + if (write(fd,buf,len) == -1) return; + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + if (write(fd,seq,strlen(seq)) == -1) return; + /* Move cursor to original position. */ + snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); + if (write(fd,seq,strlen(seq)) == -1) return; +} + +static void beep() { + fprintf(stderr, "\x7"); + fflush(stderr); +} + +static void freeCompletions(linenoiseCompletions *lc) { + size_t i; + for (i = 0; i < lc->len; i++) + free(lc->cvec[i]); + if (lc->cvec != NULL) + free(lc->cvec); +} + +static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) { + linenoiseCompletions lc = { 0, NULL }; + int nread, nwritten; + char c = 0; + + completionCallback(buf,&lc); + if (lc.len == 0) { + beep(); + } else { + size_t stop = 0, i = 0; + size_t clen; + + while(!stop) { + /* Show completion or original buffer */ + if (i < lc.len) { + clen = strlen(lc.cvec[i]); + refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols); + } else { + refreshLine(fd,prompt,buf,*len,*pos,cols); + } + + nread = read(fd,&c,1); + if (nread <= 0) { + freeCompletions(&lc); + return -1; + } + + switch(c) { + case 9: /* 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(fd,prompt,buf,*len,*pos,cols); + } + stop = 1; + break; + default: + /* Update buffer and return */ + if (i < lc.len) { + nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]); + *len = *pos = nwritten; + } + stop = 1; + break; + } + } + } + + freeCompletions(&lc); + return c; /* Return last read character */ +} + +void linenoiseClearScreen(void) { + if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) { + /* nothing to do, just to avoid warning. */ + } +} + +static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) { + size_t plen = strlen(prompt); + size_t pos = 0; + size_t len = 0; + size_t cols = getColumns(); + int history_index = 0; + size_t old_pos; + size_t diff; + + buf[0] = '\0'; + buflen--; /* Make sure there is always space for the nulterm */ + + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + linenoiseHistoryAdd(""); + + if (write(fd,prompt,plen) == -1) return -1; + while(1) { + char c; + int nread; + char seq[2], seq2[2]; + + nread = read(fd,&c,1); + if (nread <= 0) return len; + + /* 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 == 9 && completionCallback != NULL) { + c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols); + /* Return on errors */ + if (c < 0) return len; + /* Read next character when 0 */ + if (c == 0) continue; + } + + switch(c) { + case 13: /* enter */ + history_len--; + free(history[history_len]); + return (int)len; + case 3: /* ctrl-c */ + errno = EAGAIN; + return -1; + case 127: /* backspace */ + case 8: /* ctrl-h */ + if (pos > 0 && len > 0) { + memmove(buf+pos-1,buf+pos,len-pos); + pos--; + len--; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + } + break; + case 4: /* ctrl-d, remove char at right of cursor */ + if (len > 1 && pos < (len-1)) { + memmove(buf+pos,buf+pos+1,len-pos); + len--; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + } else if (len == 0) { + history_len--; + free(history[history_len]); + return -1; + } + break; + case 20: /* ctrl-t */ + if (pos > 0 && pos < len) { + int aux = buf[pos-1]; + buf[pos-1] = buf[pos]; + buf[pos] = aux; + if (pos != len-1) pos++; + refreshLine(fd,prompt,buf,len,pos,cols); + } + break; + case 2: /* ctrl-b */ + goto left_arrow; + case 6: /* ctrl-f */ + goto right_arrow; + case 16: /* ctrl-p */ + seq[1] = 65; + goto up_down_arrow; + case 14: /* ctrl-n */ + seq[1] = 66; + goto up_down_arrow; + break; + case 27: /* escape sequence */ + if (read(fd,seq,2) == -1) break; + if (seq[0] == 91 && seq[1] == 68) { +left_arrow: + /* left arrow */ + if (pos > 0) { + pos--; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else if (seq[0] == 91 && seq[1] == 67) { +right_arrow: + /* right arrow */ + if (pos != len) { + pos++; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) { +up_down_arrow: + /* up and down arrow: history */ + 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(buf); + /* Show the new entry */ + history_index += (seq[1] == 65) ? 1 : -1; + if (history_index < 0) { + history_index = 0; + break; + } else if (history_index >= history_len) { + history_index = history_len-1; + break; + } + strncpy(buf,history[history_len-1-history_index],buflen); + buf[buflen] = '\0'; + len = pos = strlen(buf); + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) { + /* extended escape */ + if (read(fd,seq2,2) == -1) break; + if (seq[1] == 51 && seq2[0] == 126) { + /* delete */ + if (len > 0 && pos < len) { + memmove(buf+pos,buf+pos+1,len-pos-1); + len--; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } + } + break; + default: + if (len < buflen) { + if (len == pos) { + buf[pos] = c; + pos++; + len++; + buf[len] = '\0'; + if (plen+len < cols) { + /* Avoid a full update of the line in the + * trivial case. */ + if (write(fd,&c,1) == -1) return -1; + } else { + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else { + memmove(buf+pos+1,buf+pos,len-pos); + buf[pos] = c; + len++; + pos++; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } + break; + case 21: /* Ctrl+u, delete the whole line. */ + buf[0] = '\0'; + pos = len = 0; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 11: /* Ctrl+k, delete from current to end of line. */ + buf[pos] = '\0'; + len = pos; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 1: /* Ctrl+a, go to the start of the line */ + pos = 0; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 5: /* ctrl+e, go to the end of the line */ + pos = len; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 12: /* ctrl+l, clear screen */ + linenoiseClearScreen(); + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 23: /* ctrl+w, delete previous word */ + old_pos = pos; + while (pos > 0 && buf[pos-1] == ' ') + pos--; + while (pos > 0 && buf[pos-1] != ' ') + pos--; + diff = old_pos - pos; + memmove(&buf[pos], &buf[old_pos], len-old_pos+1); + len -= diff; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + } + } + return len; +} + +static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { + int fd = STDIN_FILENO; + int count; + + if (buflen == 0) { + errno = EINVAL; + return -1; + } + if (!isatty(STDIN_FILENO)) { + if (fgets(buf, buflen, stdin) == NULL) return -1; + count = strlen(buf); + if (count && buf[count-1] == '\n') { + count--; + buf[count] = '\0'; + } + } else { + if (enableRawMode(fd) == -1) return -1; + count = linenoisePrompt(fd, buf, buflen, prompt); + disableRawMode(fd); + printf("\n"); + } + return count; +} + +char *linenoise(const char *prompt) { + char buf[LINENOISE_MAX_LINE]; + int count; + + if (isUnsupportedTerm()) { + size_t len; + + printf("%s",prompt); + fflush(stdout); + if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; + len = strlen(buf); + while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + len--; + buf[len] = '\0'; + } + return strdup(buf); + } else { + count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); + if (count == -1) return NULL; + return strdup(buf); + } +} + +/* Register a callback function to be called for tab-completion. */ +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { + completionCallback = fn; +} + +void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) { + size_t len = strlen(str); + char *copy = malloc(len+1); + memcpy(copy,str,len+1); + lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); + lc->cvec[lc->len++] = copy; +} + +/* 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 = malloc(sizeof(char*)*history_max_len); + if (history == NULL) return 0; + memset(history,0,(sizeof(char*)*history_max_len)); + } + 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 linenoiseHistorySetMaxLen(int len) { + char **new; + + if (len < 1) return 0; + if (history) { + int tocopy = history_len; + + new = malloc(sizeof(char*)*len); + if (new == NULL) return 0; + if (len < tocopy) tocopy = len; + memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy); + free(history); + history = new; + } + 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(char *filename) { + FILE *fp = fopen(filename,"w"); + int j; + + if (fp == NULL) return -1; + for (j = 0; j < history_len; j++) + fprintf(fp,"%s\n",history[j]); + 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(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 *p; + + p = strchr(buf,'\r'); + if (!p) p = strchr(buf,'\n'); + if (p) *p = '\0'; + linenoiseHistoryAdd(buf); + } + fclose(fp); + return 0; +} diff --git a/src/rt/linenoise/linenoise.h b/src/rt/linenoise/linenoise.h new file mode 100644 index 00000000000..0b730140856 --- /dev/null +++ b/src/rt/linenoise/linenoise.h @@ -0,0 +1,56 @@ +/* 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 + +typedef struct linenoiseCompletions { + size_t len; + char **cvec; +} linenoiseCompletions; + +typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); +void linenoiseAddCompletion(linenoiseCompletions *, char *); + +char *linenoise(const char *prompt); +int linenoiseHistoryAdd(const char *line); +int linenoiseHistorySetMaxLen(int len); +int linenoiseHistorySave(char *filename); +int linenoiseHistoryLoad(char *filename); +void linenoiseClearScreen(void); + +#endif /* __LINENOISE_H */ \ No newline at end of file diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 890aa352c92..fd91a83b51b 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -204,3 +204,11 @@ tdefl_compress_mem_to_heap tinfl_decompress_mem_to_heap rust_annihilate_box rust_gc_metadata +linenoise +linenoiseSetCompletionCallback +linenoiseAddCompletion +linenoiseHistoryAdd +linenoiseHistorySetMaxLen +linenoiseHistorySave +linenoiseHistoryLoad +linenoiseClearScreen diff --git a/src/libcore/rl.rs b/src/rusti/rl.rs similarity index 75% rename from src/libcore/rl.rs rename to src/rusti/rl.rs index 96833dd4e44..cbfa9777b29 100644 --- a/src/libcore/rl.rs +++ b/src/rusti/rl.rs @@ -1,9 +1,6 @@ use libc::{c_char, c_int}; -#[link_args = "-Llinenoise"] -#[link_name = "linenoise"] -#[abi = "cdecl"] -extern mod linenoise { +extern mod rustrt { #[legacy_exports]; fn linenoise(prompt: *c_char) -> *c_char; fn linenoiseHistoryAdd(line: *c_char) -> c_int; @@ -18,33 +15,33 @@ extern mod linenoise { /// Add a line to history pub fn add_history(line: ~str) -> bool { do str::as_c_str(line) |buf| { - linenoise::linenoiseHistoryAdd(buf) == 1 as c_int + rustrt::linenoiseHistoryAdd(buf) == 1 as c_int } } /// Set the maximum amount of lines stored pub fn set_history_max_len(len: int) -> bool { - linenoise::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int + rustrt::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int } /// Save line history to a file pub fn save_history(file: ~str) -> bool { do str::as_c_str(file) |buf| { - linenoise::linenoiseHistorySave(buf) == 1 as c_int + rustrt::linenoiseHistorySave(buf) == 1 as c_int } } /// Load line history from a file pub fn load_history(file: ~str) -> bool { do str::as_c_str(file) |buf| { - linenoise::linenoiseHistoryLoad(buf) == 1 as c_int + rustrt::linenoiseHistoryLoad(buf) == 1 as c_int } } /// Print out a prompt and then wait for input and return it pub fn read(prompt: ~str) -> Option<~str> { do str::as_c_str(prompt) |buf| unsafe { - let line = linenoise::linenoise(buf); + let line = rustrt::linenoise(buf); if line.is_null() { None } else { Some(str::raw::from_c_str(line)) } @@ -53,7 +50,7 @@ pub fn read(prompt: ~str) -> Option<~str> { /// Clear the screen pub fn clear() { - linenoise::linenoiseClearScreen(); + rustrt::linenoiseClearScreen(); } pub type CompletionCb = fn~(~str, fn(~str)); @@ -69,10 +66,10 @@ pub fn complete(cb: CompletionCb) unsafe { do cb(str::raw::from_c_str(line)) |suggestion| { do str::as_c_str(suggestion) |buf| { - linenoise::linenoiseAddCompletion(completions, buf); + rustrt::linenoiseAddCompletion(completions, buf); } } } - linenoise::linenoiseSetCompletionCallback(callback); -} + rustrt::linenoiseSetCompletionCallback(callback); +} \ No newline at end of file diff --git a/src/rusti/rusti.rc b/src/rusti/rusti.rc index 612c57add81..9d31b02ae5c 100644 --- a/src/rusti/rusti.rc +++ b/src/rusti/rusti.rc @@ -11,9 +11,6 @@ #[allow(vecs_implicitly_copyable, non_implicitly_copyable_typarams)]; -#[allow(non_camel_case_types)]; -#[allow(deprecated_mode)]; -#[allow(deprecated_pattern)]; extern mod core(vers = "0.4"); extern mod std(vers = "0.4"); @@ -22,7 +19,6 @@ extern mod syntax(vers = "0.4"); use core::*; use io::{ReaderUtil, WriterUtil}; -use std::c_vec; use rustc::back; use rustc::driver::{driver, session}; use rustc::front; @@ -34,3 +30,5 @@ use syntax::{ast, ast_util, codemap, diagnostic, fold, parse, print, visit}; use syntax::ast_util::*; use parse::token; use print::{pp, pprust}; + +mod rl; diff --git a/src/rusti/wrapper.rs b/src/rusti/wrapper.rs index 0e6510c88df..fba8849e02c 100644 --- a/src/rusti/wrapper.rs +++ b/src/rusti/wrapper.rs @@ -2,8 +2,6 @@ #[legacy_exports]; #[allow(ctypes)]; -#[allow(deprecated_mode)]; -#[allow(deprecated_pattern)]; #[allow(heap_memory)]; #[allow(implicit_copies)]; #[allow(managed_heap_memory)]; @@ -19,6 +17,6 @@ extern mod std; -fn print(result: T) { +fn print(+result: T) { io::println(fmt!("%?", result)); } From 46cf147aa206cf024ab4f0c1413216ab01da260f Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Tue, 30 Oct 2012 11:10:56 +1000 Subject: [PATCH 07/12] Remove linenoise from gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 14bc613cb56..bfcdf15fd2f 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ version.texi config.mk /rt/ /rustllvm/ -/linenoise /test/ /build/ /inst/ From 1d55a5778bc5f0ab1cac0e9f12dcc2f04ddda719 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Wed, 31 Oct 2012 10:11:19 +1000 Subject: [PATCH 08/12] rusti: Remove legacy modes and exports --- src/rusti/wrapper.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rusti/wrapper.rs b/src/rusti/wrapper.rs index fba8849e02c..6562d60ecfd 100644 --- a/src/rusti/wrapper.rs +++ b/src/rusti/wrapper.rs @@ -1,6 +1,3 @@ -#[legacy_modes]; -#[legacy_exports]; - #[allow(ctypes)]; #[allow(heap_memory)]; #[allow(implicit_copies)]; From 15880b3564efdee02b85230ef906b620a8bf4e0c Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Wed, 31 Oct 2012 13:21:28 +1000 Subject: [PATCH 09/12] rusti: Correct by-copy flag --- src/rusti/wrapper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rusti/wrapper.rs b/src/rusti/wrapper.rs index 6562d60ecfd..1e98aa81369 100644 --- a/src/rusti/wrapper.rs +++ b/src/rusti/wrapper.rs @@ -14,6 +14,6 @@ extern mod std; -fn print(+result: T) { +fn print(result: T) { io::println(fmt!("%?", result)); } From 4f56b4983bd5cb1804ef20e8ff01639b024cb58e Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sun, 4 Nov 2012 11:39:14 +1000 Subject: [PATCH 10/12] Remove linenoise files from the tidy check --- mk/tests.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mk/tests.mk b/mk/tests.mk index bfb2b63c09a..efa6d44fcc9 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -122,6 +122,7 @@ ALL_CS := $(wildcard $(S)src/rt/*.cpp \ ALL_CS := $(filter-out $(S)src/rt/bigint/bigint_ext.cpp \ $(S)src/rt/bigint/bigint_int.cpp \ $(S)src/rt/miniz.cpp \ + $(S)src/rt/linenoise/linenoise.c \ ,$(ALL_CS)) ALL_HS := $(wildcard $(S)src/rt/*.h \ $(S)src/rt/*/*.h \ @@ -135,6 +136,7 @@ ALL_HS := $(filter-out $(S)src/rt/vg/valgrind.h \ $(S)src/rt/msvc/stdint.h \ $(S)src/rt/msvc/inttypes.h \ $(S)src/rt/bigint/bigint.h \ + $(S)src/rt/linenoise/linenoise.h \ ,$(ALL_HS)) tidy: From a450119b0bd454038c483785f652419ab3c3b1c8 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sun, 4 Nov 2012 15:40:57 +1000 Subject: [PATCH 11/12] Move rusti::rl to std::rl --- src/{rusti => libstd}/rl.rs | 0 src/libstd/std.rc | 1 + src/rusti/rusti.rc | 3 +-- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{rusti => libstd}/rl.rs (100%) diff --git a/src/rusti/rl.rs b/src/libstd/rl.rs similarity index 100% rename from src/rusti/rl.rs rename to src/libstd/rl.rs diff --git a/src/libstd/std.rc b/src/libstd/std.rc index 88ef03aa534..62601ebb67b 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -82,6 +82,7 @@ pub mod arena; pub mod par; pub mod cmp; pub mod base64; +pub mod rl; #[cfg(unicode)] mod unicode; diff --git a/src/rusti/rusti.rc b/src/rusti/rusti.rc index 9d31b02ae5c..ac5edd4b48c 100644 --- a/src/rusti/rusti.rc +++ b/src/rusti/rusti.rc @@ -30,5 +30,4 @@ use syntax::{ast, ast_util, codemap, diagnostic, fold, parse, print, visit}; use syntax::ast_util::*; use parse::token; use print::{pp, pprust}; - -mod rl; +use std::rl; From 799eb105b92a8ef2238b217291ea6169d70acec4 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 4 Nov 2012 13:23:41 -0800 Subject: [PATCH 12/12] Use a linenoise with win32 support --- mk/rt.mk | 2 +- src/libstd/rl.rs | 6 - src/rt/linenoise/README.markdown | 47 + src/rt/linenoise/example.c | 30 + src/rt/linenoise/linenoise.c | 1503 ++++++++++++++++++++++-------- src/rt/linenoise/linenoise.h | 19 +- src/rt/linenoise/utf8.c | 115 +++ src/rt/linenoise/utf8.h | 79 ++ src/rt/rustrt.def.in | 1 - src/rusti/rusti.rs | 3 +- 10 files changed, 1419 insertions(+), 386 deletions(-) create mode 100644 src/rt/linenoise/README.markdown create mode 100644 src/rt/linenoise/example.c create mode 100644 src/rt/linenoise/utf8.c create mode 100644 src/rt/linenoise/utf8.h diff --git a/mk/rt.mk b/mk/rt.mk index 17f9b8b2f7b..35b8426c555 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -78,7 +78,7 @@ RUNTIME_CXXS_$(1) := \ rt/arch/$$(HOST_$(1))/context.cpp \ rt/arch/$$(HOST_$(1))/gpr.cpp -RUNTIME_CS_$(1) := rt/linenoise/linenoise.c +RUNTIME_CS_$(1) := rt/linenoise/linenoise.c rt/linenoise/utf8.c RUNTIME_S_$(1) := rt/arch/$$(HOST_$(1))/_context.S \ rt/arch/$$(HOST_$(1))/ccall.S \ diff --git a/src/libstd/rl.rs b/src/libstd/rl.rs index cbfa9777b29..dc098586382 100644 --- a/src/libstd/rl.rs +++ b/src/libstd/rl.rs @@ -9,7 +9,6 @@ extern mod rustrt { fn linenoiseHistoryLoad(file: *c_char) -> c_int; fn linenoiseSetCompletionCallback(callback: *u8); fn linenoiseAddCompletion(completions: *(), line: *c_char); - fn linenoiseClearScreen(); } /// Add a line to history @@ -48,11 +47,6 @@ pub fn read(prompt: ~str) -> Option<~str> { } } -/// Clear the screen -pub fn clear() { - rustrt::linenoiseClearScreen(); -} - pub type CompletionCb = fn~(~str, fn(~str)); fn complete_key(_v: @CompletionCb) {} diff --git a/src/rt/linenoise/README.markdown b/src/rt/linenoise/README.markdown new file mode 100644 index 00000000000..f008d2d3d5e --- /dev/null +++ b/src/rt/linenoise/README.markdown @@ -0,0 +1,47 @@ +# 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 new file mode 100644 index 00000000000..cb51a0af8f9 --- /dev/null +++ b/src/rt/linenoise/example.c @@ -0,0 +1,30 @@ +#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 index 4632f7de818..5e3216e4591 100644 --- a/src/rt/linenoise/linenoise.c +++ b/src/rt/linenoise/linenoise.c @@ -2,8 +2,9 @@ * line editing lib needs to be 20,000 lines of C code. * * You can find the latest source code at: - * - * http://github.com/antirez/linenoise + * + * 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. @@ -12,20 +13,21 @@ * * 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 @@ -37,31 +39,23 @@ * 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 * - * Todo list: - * - Switch to gets() if $TERM is something we can't support. - * - Filter bogus Ctrl+ combinations. - * - Win32 support - * * Bloat: * - Completion? - * - History search like Ctrl+r in readline? * + * Unix/termios + * ------------ * List of escape sequences used by this program, we do everything just - * with three sequences. In order to be so cheap we may have some + * 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. * - * CHA (Cursor Horizontal Absolute) - * Sequence: ESC [ n G - * Effect: moves cursor to column n - * * EL (Erase Line) * Sequence: ESC [ n K * Effect: if n is 0 or missing, clear from cursor to end of line @@ -72,6 +66,10 @@ * 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: * @@ -82,65 +80,154 @@ * 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 -#include + #include "linenoise.h" +#include "utf8.h" #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 4096 -static char *unsupported_term[] = {"dumb","cons25",NULL}; -static linenoiseCompletionCallback *completionCallback = NULL; -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 */ +#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, +}; + static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; static int history_len = 0; -char **history = NULL; +static char **history = NULL; -static void linenoiseAtExit(void); -int linenoiseHistoryAdd(const char *line); +/* 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; +#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 isUnsupportedTerm(void) { - char *term = getenv("TERM"); - int j; +static int fd_read(struct current *current); +static int getWindowSize(struct current *current); - if (term == NULL) return 0; - for (j = 0; unsupported_term[j]; j++) - if (!strcasecmp(term,unsupported_term[j])) return 1; - return 0; -} - -static void freeHistory(void) { +void linenoiseHistoryFree(void) { if (history) { int j; for (j = 0; j < history_len; j++) free(history[j]); free(history); + history = NULL; } } -static int enableRawMode(int fd) { +#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; - if (!isatty(STDIN_FILENO)) goto fatal; + 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; } - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; raw = orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, @@ -158,118 +245,675 @@ static int enableRawMode(int fd) { raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ /* put terminal in raw mode after flushing */ - if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; + if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) { + goto fatal; + } rawmode = 1; - return 0; -fatal: - errno = ENOTTY; - return -1; + current->cols = 0; + return 0; } -static void disableRawMode(int fd) { +static void disableRawMode(struct current *current) { /* Don't even check the return value as it's too late. */ - if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) + 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) { - disableRawMode(STDIN_FILENO); - freeHistory(); + if (rawmode) { + tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios); + } + linenoiseHistoryFree(); } -static int getColumns(void) { +/* gcc/glibc insists that we care about the return code of write! */ +#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 getWindowSize(struct current *current) +{ struct winsize ws; - if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80; - return ws.ws_col; + 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; } -static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) { - char seq[64]; - size_t plen = strlen(prompt); - - while((plen+pos) >= cols) { - buf++; - len--; +/** + * 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 '3': + return SPECIAL_DELETE; + 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_DELETE: + return SPECIAL_DELETE; + case VK_HOME: + return SPECIAL_HOME; + case VK_END: + return SPECIAL_END; + } + } + /* 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 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); + + /* 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 need, 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--; - } - while (plen+len > cols) { - len--; + chars--; } - /* Cursor to left edge */ - snprintf(seq,64,"\x1b[0G"); - if (write(fd,seq,strlen(seq)) == -1) return; - /* Write the prompt and the current buffer content */ - if (write(fd,prompt,strlen(prompt)) == -1) return; - if (write(fd,buf,len) == -1) return; - /* Erase to right */ - snprintf(seq,64,"\x1b[0K"); - if (write(fd,seq,strlen(seq)) == -1) return; - /* Move cursor to original position. */ - snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); - if (write(fd,seq,strlen(seq)) == -1) return; + /* 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; +} + +/** + * 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; + while (n-- && remove_char(current, pos)) { + removed++; + } + return removed; +} + +#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]); - if (lc->cvec != NULL) - free(lc->cvec); + free(lc->cvec); } -static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) { +static int completeLine(struct current *current) { linenoiseCompletions lc = { 0, NULL }; - int nread, nwritten; - char c = 0; + int c = 0; - completionCallback(buf,&lc); + completionCallback(current->buf,&lc); if (lc.len == 0) { beep(); } else { size_t stop = 0, i = 0; - size_t clen; while(!stop) { /* Show completion or original buffer */ if (i < lc.len) { - clen = strlen(lc.cvec[i]); - refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols); + 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(fd,prompt,buf,*len,*pos,cols); + refreshLine(current->prompt, current); } - nread = read(fd,&c,1); - if (nread <= 0) { - freeCompletions(&lc); - return -1; + c = fd_read(current); + if (c == -1) { + break; } switch(c) { - case 9: /* tab */ + 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(fd,prompt,buf,*len,*pos,cols); + refreshLine(current->prompt, current); } stop = 1; break; default: /* Update buffer and return */ if (i < lc.len) { - nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]); - *len = *pos = nwritten; + set_current(current,lc.cvec[i]); } stop = 1; break; @@ -281,270 +925,335 @@ static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, si return c; /* Return last read character */ } -void linenoiseClearScreen(void) { - if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) { - /* nothing to do, just to avoid warning. */ - } -} - -static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) { - size_t plen = strlen(prompt); - size_t pos = 0; - size_t len = 0; - size_t cols = getColumns(); - int history_index = 0; - size_t old_pos; - size_t diff; - - buf[0] = '\0'; - buflen--; /* Make sure there is always space for the nulterm */ - - /* The latest history entry is always our current buffer, that - * initially is just an empty string. */ - linenoiseHistoryAdd(""); - - if (write(fd,prompt,plen) == -1) return -1; - while(1) { - char c; - int nread; - char seq[2], seq2[2]; - - nread = read(fd,&c,1); - if (nread <= 0) return len; - - /* 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 == 9 && completionCallback != NULL) { - c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols); - /* Return on errors */ - if (c < 0) return len; - /* Read next character when 0 */ - if (c == 0) continue; - } - - switch(c) { - case 13: /* enter */ - history_len--; - free(history[history_len]); - return (int)len; - case 3: /* ctrl-c */ - errno = EAGAIN; - return -1; - case 127: /* backspace */ - case 8: /* ctrl-h */ - if (pos > 0 && len > 0) { - memmove(buf+pos-1,buf+pos,len-pos); - pos--; - len--; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } - break; - case 4: /* ctrl-d, remove char at right of cursor */ - if (len > 1 && pos < (len-1)) { - memmove(buf+pos,buf+pos+1,len-pos); - len--; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } else if (len == 0) { - history_len--; - free(history[history_len]); - return -1; - } - break; - case 20: /* ctrl-t */ - if (pos > 0 && pos < len) { - int aux = buf[pos-1]; - buf[pos-1] = buf[pos]; - buf[pos] = aux; - if (pos != len-1) pos++; - refreshLine(fd,prompt,buf,len,pos,cols); - } - break; - case 2: /* ctrl-b */ - goto left_arrow; - case 6: /* ctrl-f */ - goto right_arrow; - case 16: /* ctrl-p */ - seq[1] = 65; - goto up_down_arrow; - case 14: /* ctrl-n */ - seq[1] = 66; - goto up_down_arrow; - break; - case 27: /* escape sequence */ - if (read(fd,seq,2) == -1) break; - if (seq[0] == 91 && seq[1] == 68) { -left_arrow: - /* left arrow */ - if (pos > 0) { - pos--; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && seq[1] == 67) { -right_arrow: - /* right arrow */ - if (pos != len) { - pos++; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) { -up_down_arrow: - /* up and down arrow: history */ - 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(buf); - /* Show the new entry */ - history_index += (seq[1] == 65) ? 1 : -1; - if (history_index < 0) { - history_index = 0; - break; - } else if (history_index >= history_len) { - history_index = history_len-1; - break; - } - strncpy(buf,history[history_len-1-history_index],buflen); - buf[buflen] = '\0'; - len = pos = strlen(buf); - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) { - /* extended escape */ - if (read(fd,seq2,2) == -1) break; - if (seq[1] == 51 && seq2[0] == 126) { - /* delete */ - if (len > 0 && pos < len) { - memmove(buf+pos,buf+pos+1,len-pos-1); - len--; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } - } - break; - default: - if (len < buflen) { - if (len == pos) { - buf[pos] = c; - pos++; - len++; - buf[len] = '\0'; - if (plen+len < cols) { - /* Avoid a full update of the line in the - * trivial case. */ - if (write(fd,&c,1) == -1) return -1; - } else { - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else { - memmove(buf+pos+1,buf+pos,len-pos); - buf[pos] = c; - len++; - pos++; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } - break; - case 21: /* Ctrl+u, delete the whole line. */ - buf[0] = '\0'; - pos = len = 0; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 11: /* Ctrl+k, delete from current to end of line. */ - buf[pos] = '\0'; - len = pos; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 1: /* Ctrl+a, go to the start of the line */ - pos = 0; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 5: /* ctrl+e, go to the end of the line */ - pos = len; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 12: /* ctrl+l, clear screen */ - linenoiseClearScreen(); - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 23: /* ctrl+w, delete previous word */ - old_pos = pos; - while (pos > 0 && buf[pos-1] == ' ') - pos--; - while (pos > 0 && buf[pos-1] != ' ') - pos--; - diff = old_pos - pos; - memmove(&buf[pos], &buf[old_pos], len-old_pos+1); - len -= diff; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - } - } - return len; -} - -static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { - int fd = STDIN_FILENO; - int count; - - if (buflen == 0) { - errno = EINVAL; - return -1; - } - if (!isatty(STDIN_FILENO)) { - if (fgets(buf, buflen, stdin) == NULL) return -1; - count = strlen(buf); - if (count && buf[count-1] == '\n') { - count--; - buf[count] = '\0'; - } - } else { - if (enableRawMode(fd) == -1) return -1; - count = linenoisePrompt(fd, buf, buflen, prompt); - disableRawMode(fd); - printf("\n"); - } - return count; -} - -char *linenoise(const char *prompt) { - char buf[LINENOISE_MAX_LINE]; - int count; - - if (isUnsupportedTerm()) { - size_t len; - - printf("%s",prompt); - fflush(stdout); - if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; - len = strlen(buf); - while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { - len--; - buf[len] = '\0'; - } - return strdup(buf); - } else { - count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); - if (count == -1) return NULL; - return strdup(buf); - } -} - /* Register a callback function to be called for tab-completion. */ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { completionCallback = fn; } -void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) { - size_t len = strlen(str); - char *copy = malloc(len+1); - memcpy(copy,str,len+1); - lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); - lc->cvec[lc->len++] = copy; +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 linenoisePrompt(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 ctrl('W'): /* ctrl-w */ + /* 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) { + c = get_char(current, current->pos); + remove_char(current, current->pos); + 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 ctrl('P'): + case SPECIAL_UP: + dir = 1; + case ctrl('N'): + case SPECIAL_DOWN: + 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. */ + if (remove_chars(current, 0, current->pos)) { + refreshLine(current->prompt, current); + } + break; + case ctrl('K'): /* Ctrl+k, delete from current to end of line. */ + if (remove_chars(current, current->pos, current->chars - current->pos)) { + 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; +} + +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; + + count = linenoisePrompt(¤t); + disableRawMode(¤t); + printf("\n"); + if (count == -1) { + return NULL; + } + } + return strdup(buf); } /* Using a circular buffer is smarter, but a bit more complex to handle. */ @@ -553,10 +1262,16 @@ int linenoiseHistoryAdd(const char *line) { if (history_max_len == 0) return 0; if (history == NULL) { - history = malloc(sizeof(char*)*history_max_len); + 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) { @@ -570,18 +1285,18 @@ int linenoiseHistoryAdd(const char *line) { } int linenoiseHistorySetMaxLen(int len) { - char **new; + char **newHistory; if (len < 1) return 0; if (history) { int tocopy = history_len; - new = malloc(sizeof(char*)*len); - if (new == NULL) return 0; + newHistory = (char **)malloc(sizeof(char*)*len); + if (newHistory == NULL) return 0; if (len < tocopy) tocopy = len; - memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy); + memcpy(newHistory,history+(history_max_len-tocopy), sizeof(char*)*tocopy); free(history); - history = new; + history = newHistory; } history_max_len = len; if (history_len > history_max_len) @@ -591,13 +1306,32 @@ int linenoiseHistorySetMaxLen(int len) { /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ -int linenoiseHistorySave(char *filename) { +int linenoiseHistorySave(const char *filename) { FILE *fp = fopen(filename,"w"); int j; - + if (fp == NULL) return -1; - for (j = 0; j < history_len; j++) - fprintf(fp,"%s\n",history[j]); + 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; } @@ -607,20 +1341,51 @@ int linenoiseHistorySave(char *filename) { * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ -int linenoiseHistoryLoad(char *filename) { +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 *p; - - p = strchr(buf,'\r'); - if (!p) p = strchr(buf,'\n'); - if (p) *p = '\0'; + 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 index 0b730140856..59f28976d67 100644 --- a/src/rt/linenoise/linenoise.h +++ b/src/rt/linenoise/linenoise.h @@ -9,18 +9,18 @@ * 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 @@ -37,6 +37,7 @@ #ifndef __LINENOISE_H #define __LINENOISE_H +#ifndef NO_COMPLETION typedef struct linenoiseCompletions { size_t len; char **cvec; @@ -44,13 +45,15 @@ typedef struct linenoiseCompletions { typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); -void linenoiseAddCompletion(linenoiseCompletions *, char *); +void linenoiseAddCompletion(linenoiseCompletions *, const char *); +#endif char *linenoise(const char *prompt); int linenoiseHistoryAdd(const char *line); int linenoiseHistorySetMaxLen(int len); -int linenoiseHistorySave(char *filename); -int linenoiseHistoryLoad(char *filename); -void linenoiseClearScreen(void); +int linenoiseHistorySave(const char *filename); +int linenoiseHistoryLoad(const char *filename); +void linenoiseHistoryFree(void); +char **linenoiseHistory(int *len); -#endif /* __LINENOISE_H */ \ No newline at end of file +#endif /* __LINENOISE_H */ diff --git a/src/rt/linenoise/utf8.c b/src/rt/linenoise/utf8.c new file mode 100644 index 00000000000..26924b46c19 --- /dev/null +++ b/src/rt/linenoise/utf8.c @@ -0,0 +1,115 @@ +/** + * 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 new file mode 100644 index 00000000000..9537939876a --- /dev/null +++ b/src/rt/linenoise/utf8.h @@ -0,0 +1,79 @@ +#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/rustrt.def.in b/src/rt/rustrt.def.in index fd91a83b51b..0668479a48d 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -211,4 +211,3 @@ linenoiseHistoryAdd linenoiseHistorySetMaxLen linenoiseHistorySave linenoiseHistoryLoad -linenoiseClearScreen diff --git a/src/rusti/rusti.rs b/src/rusti/rusti.rs index 2fa4684b322..5f1cad3ae17 100644 --- a/src/rusti/rusti.rs +++ b/src/rusti/rusti.rs @@ -243,7 +243,8 @@ fn run_cmd(repl: &mut Repl, _in: io::Reader, _out: io::Writer, repl.view_items = ~""; repl.stmts = ~""; - rl::clear(); + // FIXME: Win32 version of linenoise doesn't have the required function + //rl::clear(); } ~"help" => { io::println(~":clear - clear the screen\n" +