From de86403f330e68df8fc4aee00df98374b7842d0d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 6 Mar 2015 12:18:44 +0100 Subject: [PATCH] cmd/ethereum: fix JS REPL exit and add support for dumb terminals It is now possible to exit the REPL using Ctrl-C, Ctrl-D or by typing "exit". --- cmd/ethereum/js.go | 84 ++++++++++++++++++++++++++++++-------------- cmd/ethereum/main.go | 7 ++-- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index e16ee171e..9125ccbba 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -18,9 +18,11 @@ package main import ( + "bufio" "fmt" "io/ioutil" "os" + "os/signal" "path" "strings" @@ -55,44 +57,38 @@ type repl struct { ethereum *eth.Ethereum xeth *xeth.XEth prompt string - histfile *os.File lr *liner.State - running bool } -func newREPL(ethereum *eth.Ethereum) *repl { - hist, err := os.OpenFile(path.Join(ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - panic(err) - } +func runREPL(ethereum *eth.Ethereum) { xeth := xeth.New(ethereum) repl := &repl{ re: javascript.NewJSRE(xeth), xeth: xeth, ethereum: ethereum, prompt: "> ", - histfile: hist, - lr: liner.NewLiner(), } repl.initStdFuncs() - return repl -} - -func (self *repl) Start() { - if !self.running { - self.running = true - self.lr.ReadHistory(self.histfile) - go self.read() + if !liner.TerminalSupported() { + repl.dumbRead() + } else { + lr := liner.NewLiner() + defer lr.Close() + lr.SetCtrlCAborts(true) + repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) + repl.read(lr) + repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) } } -func (self *repl) Stop() { - if self.running { - self.running = false - self.histfile.Truncate(0) - self.lr.WriteHistory(self.histfile) - self.histfile.Close() +func (self *repl) withHistory(op func(*os.File)) { + hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + fmt.Printf("unable to open history file: %v\n", err) + return } + op(hist) + hist.Close() } func (self *repl) parseInput(code string) { @@ -126,9 +122,9 @@ func (self *repl) setIndent() { } } -func (self *repl) read() { +func (self *repl) read(lr *liner.State) { for { - input, err := self.lr.Prompt(self.prompt) + input, err := lr.Prompt(self.prompt) if err != nil { return } @@ -139,17 +135,51 @@ func (self *repl) read() { self.setIndent() if indentCount <= 0 { if input == "exit" { - self.Stop() return } hist := str[:len(str)-1] - self.lr.AppendHistory(hist) + lr.AppendHistory(hist) self.parseInput(str) str = "" } } } +func (self *repl) dumbRead() { + fmt.Println("Unsupported terminal, line editing will not work.") + + // process lines + readDone := make(chan struct{}) + go func() { + r := bufio.NewReader(os.Stdin) + loop: + for { + fmt.Print(self.prompt) + line, err := r.ReadString('\n') + switch { + case err != nil || line == "exit": + break loop + case line == "": + continue + default: + self.parseInput(line + "\n") + } + } + close(readDone) + }() + + // wait for Ctrl-C + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, os.Kill) + defer signal.Stop(sigc) + + select { + case <-readDone: + case <-sigc: + os.Stdin.Close() // terminate read + } +} + func (self *repl) printValue(v interface{}) { method, _ := self.re.Vm.Get("prettyPrint") v, err := self.re.Vm.ToValue(v) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index c85caf229..1133bd6f7 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -125,7 +125,6 @@ runtime will execute the file and exit. func main() { runtime.GOMAXPROCS(runtime.NumCPU()) defer logger.Flush() - utils.HandleInterrupt() if err := app.Run(os.Args); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) @@ -134,6 +133,7 @@ func main() { func run(ctx *cli.Context) { fmt.Printf("Welcome to the FRONTIER\n") + utils.HandleInterrupt() eth := utils.GetEthereum(ClientIdentifier, Version, ctx) startEth(ctx, eth) // this blocks the thread @@ -144,9 +144,8 @@ func runjs(ctx *cli.Context) { eth := utils.GetEthereum(ClientIdentifier, Version, ctx) startEth(ctx, eth) if len(ctx.Args()) == 0 { - repl := newREPL(eth) - utils.RegisterInterrupt(func(os.Signal) { repl.Stop() }) - repl.Start() + runREPL(eth) + eth.Stop() eth.WaitForShutdown() } else if len(ctx.Args()) == 1 { execJsFile(eth, ctx.Args()[0])