diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go
index e16ee171eba742a4d2bf37ef232e8013f0841274..9125ccbba844e2bba740fc13f4be0f07a9938850 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 c85caf229a70ed2dc6c16d4002c2006fbb52ff84..1133bd6f7264543bb524d7741079291f4126de51 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])