You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2018/01/07 18:54:55 UTC

[GitHub] rymanluk closed pull request #55: Newtmgr interactive mode used for COAP operations

rymanluk closed pull request #55: Newtmgr interactive mode used for COAP operations
URL: https://github.com/apache/mynewt-newtmgr/pull/55
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/Gopkg.lock b/Gopkg.lock
index f359aec..f5daf15 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -25,6 +25,12 @@
   revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
   version = "v1.0"
 
+[[projects]]
+  branch = "master"
+  name = "github.com/flynn/go-shlex"
+  packages = ["."]
+  revision = "3f9db97f856818214da2e1057f8ad84803971cff"
+
 [[projects]]
   name = "github.com/inconshreveable/mousetrap"
   packages = ["."]
@@ -163,12 +169,24 @@
   packages = ["unix","windows"]
   revision = "ab9e364efd8b52800ff7ee48a9ffba4e0ed78dfb"
 
+[[projects]]
+  name = "gopkg.in/abiosoft/ishell.v1"
+  packages = ["."]
+  revision = "50251d04cb42673a9df901983eeaf9f369c82551"
+  version = "v1"
+
 [[projects]]
   name = "gopkg.in/fsnotify.v1"
   packages = ["."]
   revision = "629574ca2a5df945712d3079857300b5e4da0236"
   version = "v1.4.2"
 
+[[projects]]
+  name = "gopkg.in/readline.v1"
+  packages = ["."]
+  revision = "62c6fe6193755f722b8b8788aa7357be55a50ff1"
+  version = "v1.4"
+
 [[projects]]
   branch = "master"
   name = "mynewt.apache.org/newt"
@@ -178,6 +196,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "6a7245247cf11745348daa479eb65c063d23b755b400d35003cac58f621d8f0f"
+  inputs-digest = "006ff4573b02a7ab7a8170515b246f4e01c68c9a1b8dc4881ab5d6b3663a502d"
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/newtmgr/bll/bll_sesn.go b/newtmgr/bll/bll_sesn.go
index 51c8d4c..995b1c5 100644
--- a/newtmgr/bll/bll_sesn.go
+++ b/newtmgr/bll/bll_sesn.go
@@ -419,6 +419,27 @@ func (s *BllSesn) TxCoapOnce(m coap.Message, resType sesn.ResourceType,
 	}
 }
 
+func (s *BllSesn) TxCoapObserve(m coap.Message, resType sesn.ResourceType,
+	opt sesn.TxOptions, NotifCb sesn.GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+	chr, err := s.resReqChr(resType)
+	if err != nil {
+		return 0, nil, nil, err
+	}
+
+	txRaw := func(b []byte) error {
+		return s.txWriteCharacteristic(chr, b, !s.cfg.WriteRsp)
+	}
+
+	rsp, err := s.txvr.TxOicObserve(txRaw, m, s.MtuOut(), opt.Timeout, NotifCb, stopsignal)
+	if err != nil {
+		return 0, nil, nil, err
+	} else if rsp == nil {
+		return 0, nil, nil, nil
+	} else {
+		return rsp.Code(), rsp.Payload(), rsp.Token(), nil
+	}
+}
+
 func (s *BllSesn) MgmtProto() sesn.MgmtProto {
 	return s.cfg.MgmtProto
 }
diff --git a/newtmgr/cli/commands.go b/newtmgr/cli/commands.go
index 70af24e..53d474b 100644
--- a/newtmgr/cli/commands.go
+++ b/newtmgr/cli/commands.go
@@ -95,6 +95,7 @@ func Commands() *cobra.Command {
 	nmCmd.AddCommand(connProfileCmd())
 	nmCmd.AddCommand(echoCmd())
 	nmCmd.AddCommand(resCmd())
+	nmCmd.AddCommand(interactiveCmd())
 
 	return nmCmd
 }
diff --git a/newtmgr/cli/interactive.go b/newtmgr/cli/interactive.go
new file mode 100644
index 0000000..a8a957e
--- /dev/null
+++ b/newtmgr/cli/interactive.go
@@ -0,0 +1,517 @@
+package cli
+
+import (
+	"container/list"
+	"fmt"
+	"gopkg.in/abiosoft/ishell.v1"
+	"github.com/runtimeco/go-coap"
+	"github.com/spf13/cobra"
+	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/newtmgr/newtmgr/nmutil"
+	"mynewt.apache.org/newtmgr/nmxact/nmxutil"
+	"mynewt.apache.org/newtmgr/nmxact/sesn"
+	"mynewt.apache.org/newtmgr/nmxact/xact"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var ObserverId int
+
+type ObserveElem struct {
+	Id         int
+	Token      []byte
+	Stopsignal chan int
+	Path       string
+}
+
+var ResourceType string
+var ResourcePath string
+var ObserversList *list.List
+
+func addObserver(path string, token []byte, stopsignal chan int) ObserveElem {
+	o := ObserveElem{
+		Id:         ObserverId,
+		Token:      token,
+		Stopsignal: stopsignal,
+		Path:       path,
+	}
+
+	ObserverId++
+	ObserversList.PushBack(o)
+	return o
+}
+
+func notificationCb(path string, Code coap.COAPCode, value []byte, token []byte) {
+	fmt.Println("Notification received:")
+	fmt.Println("Code:", Code)
+	fmt.Println("Token: [", token, "]")
+	fmt.Printf("%s\n", resResponseStr(path, value))
+	fmt.Println()
+}
+
+func copyFromMap(m map[string]interface{}, key string) (value string) {
+
+	if m[key] == nil {
+		return ""
+	}
+
+	v, ok := m[key].(string)
+	if ok {
+		return v
+	} else {
+		return ""
+	}
+}
+
+func hasStoredParams() bool {
+
+	if strings.Compare(ResourcePath, "") == 0 || strings.Compare(ResourceType, "") == 0 {
+		return false
+	}
+
+	return true
+}
+
+func getPathAndType(m map[string]interface{}) {
+
+	rpath := copyFromMap(m, "path")
+	rtype := copyFromMap(m, "type")
+
+	if strings.Compare(rpath, "") != 0 {
+		ResourcePath = rpath
+	}
+
+	if strings.Compare(rtype, "") != 0 {
+		ResourceType = rtype
+	}
+}
+
+func getCmdCommon(c *ishell.Context, observe int, token []byte) error {
+
+	rt, err := sesn.ParseResType(ResourceType)
+	if err != nil {
+		c.Println("Incorrect resource type")
+		return err
+	}
+
+	s, err := GetSesn()
+	if err != nil {
+		nmUsage(nil, err)
+	}
+
+	cmd := xact.NewGetResCmd()
+	cmd.SetTxOptions(nmutil.TxOptions())
+	cmd.Path = ResourcePath
+	cmd.Observe = observe
+	cmd.Typ = rt
+	cmd.NotifyFunc = notificationCb
+	cmd.StopSignal = make(chan int)
+	cmd.Token = token
+
+	res, err := cmd.Run(s)
+	if err != nil {
+		c.Println("Error:", err)
+		return err
+	}
+
+	sres := res.(*xact.GetResResult)
+	if sres.Status() != 0 {
+		fmt.Printf("Error: %s (%d)\n",
+			coap.COAPCode(sres.Status()), sres.Status())
+		return err
+	}
+
+	if observe == 0 {
+		o := addObserver(cmd.Path, sres.Token, cmd.StopSignal)
+		c.Println("Observer added:")
+		c.Println("id:", o.Id, "path:", o.Path, "token:", o.Token)
+		c.Println()
+	}
+
+	if sres.Value != nil {
+		fmt.Printf("%s\n", resResponseStr(cmd.Path, sres.Value))
+	}
+
+	return nil
+}
+
+func getCmd(c *ishell.Context) {
+
+	m, err := extractResKv(c.Args)
+	if err != nil || len(c.Args) == 0 {
+		c.Println("Incorrect or no parameters provided ... using cached ones")
+	} else {
+		getPathAndType(m)
+	}
+
+	if hasStoredParams() == false {
+		c.Println("Missing resource path or type")
+		c.Println(c.HelpText())
+		return
+	}
+
+	c.Println(m)
+
+	c.Println("command: ", c.Cmd.Name)
+	c.Println("path: ", ResourcePath)
+	c.Println("type: ", ResourceType)
+	c.Println()
+
+	getCmdCommon(c, -1, nil)
+}
+
+func registerCmd(c *ishell.Context) {
+	m, err := extractResKv(c.Args)
+	if err != nil || len(c.Args) == 0 {
+		c.Println("Incorrect or no parameters provided ... using cached ones")
+	} else {
+		getPathAndType(m)
+	}
+
+	if hasStoredParams() == false {
+		c.Println("Missing resource path or type")
+		c.Println(c.HelpText())
+		return
+	}
+
+	c.Println(m)
+
+	c.Println("Register for notifications")
+	c.Println("path: ", ResourcePath)
+	c.Println("type: ", ResourceType)
+	c.Println()
+
+	getCmdCommon(c, 0, nil)
+}
+
+func unregisterCmd(c *ishell.Context) {
+	m, err := extractResKv(c.Args)
+	if err != nil || len(c.Args) == 0 {
+		c.Println("Incorrect or no parameters provided ... using cached ones")
+	} else {
+		getPathAndType(m)
+	}
+
+	if hasStoredParams() == false {
+		c.Println("Missing resource path or type")
+		c.Println(c.HelpText())
+		return
+	}
+
+	if m["id"] == nil {
+		c.Println(c.HelpText())
+		return
+	}
+
+	idstr, ok := m["id"].(string)
+	if ok == false {
+		c.Println(c.HelpText())
+		return
+	}
+
+	id, err := strconv.Atoi(idstr)
+	if err != nil {
+		c.Println(c.HelpText())
+		return
+	}
+
+	var e ObserveElem
+	found := false
+	elem := ObserversList.Front()
+	for elem != nil && found == false {
+		e, ok = (elem.Value).(ObserveElem)
+		if ok && e.Id == id {
+			found = true
+		} else {
+			elem = elem.Next()
+		}
+	}
+
+	if found == false {
+		c.Println("Observer id:", id, "not found")
+		return
+	}
+
+	/* Stop listen. Sleep to allow to close existing listener before we send next GET request */
+	e.Stopsignal <- 1
+	time.Sleep(1)
+
+	c.Println("Unegister for notifications")
+	c.Println("id: ", e.Id)
+	c.Println("path: ", e.Path)
+	c.Println("token: ", e.Token)
+	c.Println()
+
+	err = getCmdCommon(c, 1, e.Token)
+	if err == nil {
+		ObserversList.Remove(elem)
+	}
+}
+
+func getUriParams(c *ishell.Context) (map[string]interface{}, error) {
+
+	c.ShowPrompt(false)
+	defer c.ShowPrompt(true)
+
+	c.Println("provide ", c.Cmd.Name, " parameters in format key=value [key=value]")
+	pstr := c.ReadLine()
+	params := strings.Split(pstr, "")
+
+	return extractResKv(params)
+}
+
+func putCmd(c *ishell.Context) {
+
+	m, err := extractResKv(c.Args)
+	if err != nil || len(c.Args) == 0 {
+		c.Println("Incorrect or no parameters provided ... using cached ones")
+	} else {
+		getPathAndType(m)
+	}
+
+	if hasStoredParams() == false {
+		c.Println("Missing resource path or type")
+		c.Println(c.HelpText())
+		return
+	}
+
+	rt, err := sesn.ParseResType(ResourceType)
+	if err != nil {
+		c.Println("Incorrect resource type")
+		return
+	}
+
+	m, err = getUriParams(c)
+	if err != nil {
+		c.Println(c.HelpText())
+		return
+	}
+
+	s, err := GetSesn()
+	if err != nil {
+		nmUsage(nil, err)
+	}
+
+	b, err := nmxutil.EncodeCborMap(m)
+	if err != nil {
+		nmUsage(nil, util.ChildNewtError(err))
+	}
+
+	cmd := xact.NewPutResCmd()
+	cmd.SetTxOptions(nmutil.TxOptions())
+	cmd.Path = ResourcePath
+	cmd.Typ = rt
+	cmd.Value = b
+
+	res, err := cmd.Run(s)
+	if err != nil {
+		c.Println("Error: ", err)
+		return
+	}
+
+	sres := res.(*xact.PutResResult)
+	if sres.Status() != 0 {
+		fmt.Printf("Error: %s (%d)\n",
+			coap.COAPCode(sres.Status()), sres.Status())
+		return
+	}
+
+	if sres.Value != nil {
+		fmt.Printf("%s\n", resResponseStr(cmd.Path, sres.Value))
+	}
+}
+
+func postCmd(c *ishell.Context) {
+
+	m, err := extractResKv(c.Args)
+	if err != nil || len(c.Args) == 0 {
+		c.Println("Incorrect or no parameters provided ... using cached ones")
+	} else {
+		getPathAndType(m)
+	}
+
+	if hasStoredParams() == false {
+		c.Println("Missing resource path or type")
+		c.Println(c.HelpText())
+		return
+	}
+
+	rt, err := sesn.ParseResType(ResourceType)
+	if err != nil {
+		c.Println("Incorrect resource type")
+		return
+	}
+
+	m, err = getUriParams(c)
+	if err != nil {
+		c.Println(c.HelpText())
+		return
+	}
+
+	b, err := nmxutil.EncodeCborMap(m)
+	if err != nil {
+		nmUsage(nil, util.ChildNewtError(err))
+	}
+
+	s, err := GetSesn()
+	if err != nil {
+		nmUsage(nil, err)
+	}
+
+	cmd := xact.NewPostResCmd()
+	cmd.SetTxOptions(nmutil.TxOptions())
+	cmd.Path = ResourcePath
+	cmd.Typ = rt
+	cmd.Value = b
+
+	res, err := cmd.Run(s)
+	if err != nil {
+		c.Println("Error: ", err)
+		return
+	}
+
+	sres := res.(*xact.PostResResult)
+	if sres.Status() != 0 {
+		fmt.Printf("Error: %s (%d)\n",
+			coap.COAPCode(sres.Status()), sres.Status())
+		return
+	}
+
+	if sres.Value != nil {
+		fmt.Printf("%s\n", resResponseStr(cmd.Path, sres.Value))
+	}
+}
+
+func deleteCmd(c *ishell.Context) {
+
+	m, err := extractResKv(c.Args)
+	if err != nil || len(c.Args) == 0 {
+		c.Println("Incorrect or no parameters provided ... using cached ones")
+	} else {
+		getPathAndType(m)
+	}
+
+	if hasStoredParams() == false {
+		c.Println("Missing resource path or type")
+		c.Println(c.HelpText())
+		return
+	}
+
+	s, err := GetSesn()
+	if err != nil {
+		nmUsage(nil, err)
+	}
+
+	rt, err := sesn.ParseResType(ResourceType)
+	if err != nil {
+		c.Println("Incorrect resource type")
+		return
+	}
+
+	cmd := xact.NewDeleteResCmd()
+	cmd.SetTxOptions(nmutil.TxOptions())
+	cmd.Path = ResourcePath
+	cmd.Typ = rt
+
+	res, err := cmd.Run(s)
+	if err != nil {
+		c.Println("Error: ", err)
+		return
+	}
+
+	sres := res.(*xact.DeleteResResult)
+	if sres.Status() != 0 {
+		fmt.Printf("Error: %s (%d)\n",
+			coap.COAPCode(sres.Status()), sres.Status())
+		return
+	}
+
+	if sres.Value != nil {
+		fmt.Printf("%s\n", resResponseStr(cmd.Path, sres.Value))
+	}
+}
+
+func printObservers(c *ishell.Context) {
+
+	elem := ObserversList.Front()
+	for elem != nil {
+		e, ok := (elem.Value).(ObserveElem)
+		if ok {
+			c.Println("id:", e.Id, ", path:", e.Path, ", token:", e.Token)
+		}
+		elem = elem.Next()
+	}
+}
+
+func startInteractive(cmd *cobra.Command, args []string) {
+
+	// create new shell.
+	// by default, new shell includes 'exit', 'help' and 'clear' commands.
+	shell := ishell.New()
+	shell.SetPrompt("> ")
+
+	ObserversList = list.New()
+
+	// display welcome info.
+	shell.Println()
+	shell.Println(" Newtmgr shell mode for COAP:")
+	shell.Println("	Connection profile: ", nmutil.ConnProfile)
+	shell.Println()
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "get",
+		Help: "Send a CoAP GET request: get path=v type=v",
+		Func: getCmd,
+	})
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "put",
+		Help: "Send a CoAP PUT request: path=v type=v <you will be asked for params>",
+		Func: putCmd,
+	})
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "post",
+		Help: "Send a CoAP POST request: post type=v path=v <you will be asked for params>",
+		Func: postCmd,
+	})
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "delete",
+		Help: "Send a CoAP POST request: delete type=v path=v",
+		Func: deleteCmd,
+	})
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "reg",
+		Help: "Register for notifications: req path=v type=v",
+		Func: registerCmd,
+	})
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "unreg",
+		Help: "Unregister from notifications (id means observer id): unreq id=v",
+		Func: unregisterCmd,
+	})
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "observers",
+		Help: "Print registered observers: observers",
+		Func: printObservers,
+	})
+
+	shell.Run()
+	shell.Close()
+}
+
+func interactiveCmd() *cobra.Command {
+
+	shellCmd := &cobra.Command{
+		Use:   "interactive",
+		Short: "Run newtmgr interactive mode (used for COAP only)",
+		Run:   startInteractive,
+	}
+
+	return shellCmd
+}
diff --git a/newtmgr/cli/res.go b/newtmgr/cli/res.go
index af9cef0..1c92f3c 100644
--- a/newtmgr/cli/res.go
+++ b/newtmgr/cli/res.go
@@ -111,6 +111,7 @@ func resGetCmd(cmd *cobra.Command, args []string) {
 	c := xact.NewGetResCmd()
 	c.SetTxOptions(nmutil.TxOptions())
 	c.Path = path
+	c.Observe = -1
 	c.Typ = rt
 
 	res, err := c.Run(s)
diff --git a/nmxact/mgmt/transceiver.go b/nmxact/mgmt/transceiver.go
index 2f30e70..e90fa11 100644
--- a/nmxact/mgmt/transceiver.go
+++ b/nmxact/mgmt/transceiver.go
@@ -217,6 +217,80 @@ func (t *Transceiver) TxOic(txCb TxFn, req coap.Message, mtu int,
 	}
 }
 
+func (t *Transceiver) TxOicObserve(txCb TxFn, req coap.Message, mtu int,
+	timeout time.Duration, NotifyCb sesn.GetNotifyCb, stopsignal chan int) (coap.Message, error) {
+
+	b, err := nmcoap.Encode(req)
+	if err != nil {
+		return nil, err
+	}
+
+	var ol *nmcoap.Listener
+
+	ol, err = t.od.AddOicListener(req.Token())
+	if err != nil {
+		return nil, err
+	}
+
+	log.Debugf("Tx OIC request: %s", hex.Dump(b))
+	frags := nmxutil.Fragment(b, mtu)
+	for _, frag := range frags {
+		if err := txCb(frag); err != nil {
+			t.od.RemoveOicListener(req.Token())
+			return nil, err
+		}
+	}
+
+	var rsp coap.Message
+
+	first := make(chan int)
+	iter := 0
+
+	go func() {
+		defer t.od.RemoveOicListener(req.Token())
+
+		for {
+			select {
+			case err = <-ol.ErrChan:
+				log.Debugf("Error: %s", err)
+				first <- 2
+				return
+			case rsp = <-ol.RspChan:
+				if iter == 0 {
+					first <- 1
+					iter = 1
+				} else {
+					NotifyCb(req.PathString(), rsp.Code(), rsp.Payload(), rsp.Token())
+				}
+
+			case _, ok := <-ol.AfterTimeout(timeout):
+				if ok && iter == 0 {
+					err = nmxutil.NewRspTimeoutError("OIC timeout")
+					first <- 2
+					return
+				} else {
+					log.Debugf("Timeout")
+				}
+
+			case <-stopsignal:
+				/* Observing stopped by user */
+				return
+			}
+		}
+	}()
+
+	for {
+		select {
+		case a := <-first:
+			if a == 1 {
+				return rsp, nil
+			} else {
+				return nil, err
+			}
+		}
+	}
+}
+
 func (t *Transceiver) DispatchNmpRsp(data []byte) {
 	if t.nd != nil {
 		t.nd.Dispatch(data)
diff --git a/nmxact/mtech_lora/mtech_lora_sesn.go b/nmxact/mtech_lora/mtech_lora_sesn.go
index 7936275..09ad4a5 100644
--- a/nmxact/mtech_lora/mtech_lora_sesn.go
+++ b/nmxact/mtech_lora/mtech_lora_sesn.go
@@ -266,6 +266,11 @@ func (s *LoraSesn) TxCoapOnce(m coap.Message, resType sesn.ResourceType,
 	}
 }
 
+func (s *LoraSesn) TxCoapObserve(m coap.Message, resType sesn.ResourceType,
+	opt sesn.TxOptions, NotifCb sesn.GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+	return 0, nil, nil, nil
+}
+
 func (s *LoraSesn) MgmtProto() sesn.MgmtProto {
 	return s.cfg.MgmtProto
 }
diff --git a/nmxact/nmble/ble_sesn.go b/nmxact/nmble/ble_sesn.go
index d47a3ad..42fd5fb 100644
--- a/nmxact/nmble/ble_sesn.go
+++ b/nmxact/nmble/ble_sesn.go
@@ -113,3 +113,10 @@ func (s *BleSesn) TxCoapOnce(m coap.Message,
 
 	return s.Ns.TxCoapOnce(m, resType, opt)
 }
+
+func (s *BleSesn) TxCoapObserve(m coap.Message,
+	resType sesn.ResourceType,
+	opt sesn.TxOptions, NotifCb sesn.GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+
+	return s.Ns.TxCoapObserve(m, resType, opt, NotifCb, stopsignal)
+}
diff --git a/nmxact/nmble/naked_sesn.go b/nmxact/nmble/naked_sesn.go
index 6d55672..b93fb90 100644
--- a/nmxact/nmble/naked_sesn.go
+++ b/nmxact/nmble/naked_sesn.go
@@ -386,6 +386,55 @@ func (s *NakedSesn) TxCoapOnce(m coap.Message,
 	return rspCode, rspPayload, nil
 }
 
+func (s *NakedSesn) TxCoapObserve(m coap.Message, resType sesn.ResourceType, opt sesn.TxOptions,
+	NotifyCb sesn.GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+	if err := s.failIfNotOpen(); err != nil {
+		return 0, nil, nil, err
+	}
+
+	var rspCode coap.COAPCode
+	var rspPayload []byte
+	var rspToken []byte
+
+	fn := func() error {
+		chrId := ResChrReqIdLookup(s.mgmtChrs, resType)
+		chr, err := s.getChr(chrId)
+		if err != nil {
+			return err
+		}
+
+		encReqd, authReqd, err := ResTypeSecReqs(resType)
+		if err != nil {
+			return err
+		}
+		if err := s.ensureSecurity(encReqd, authReqd); err != nil {
+			return err
+		}
+
+		txRaw := func(b []byte) error {
+			if s.cfg.Ble.WriteRsp {
+				return s.conn.WriteChr(chr, b, "coap")
+			} else {
+				return s.conn.WriteChrNoRsp(chr, b, "coap")
+			}
+		}
+
+		rsp, err := s.txvr.TxOicObserve(txRaw, m, s.MtuOut(), opt.Timeout, NotifyCb, stopsignal)
+		if err == nil && rsp != nil {
+			rspCode = rsp.Code()
+			rspPayload = rsp.Payload()
+			rspToken = rsp.Token()
+		}
+		return err
+	}
+
+	if err := s.runTask(fn); err != nil {
+		return 0, nil, nil, err
+	}
+
+	return rspCode, rspPayload, rspToken, nil
+}
+
 func (s *NakedSesn) AbortRx(seq uint8) error {
 	if err := s.failIfNotOpen(); err != nil {
 		return err
diff --git a/nmxact/nmcoap/dispatch.go b/nmxact/nmcoap/dispatch.go
index 311e5b1..7dfb4f8 100644
--- a/nmxact/nmcoap/dispatch.go
+++ b/nmxact/nmcoap/dispatch.go
@@ -66,7 +66,9 @@ func NewListener() *Listener {
 
 func (ol *Listener) AfterTimeout(tmo time.Duration) <-chan time.Time {
 	fn := func() {
-		ol.tmoChan <- time.Now()
+		if ol.tmoChan != nil {
+			ol.tmoChan <- time.Now()
+		}
 	}
 	ol.timer = time.AfterFunc(tmo, fn)
 	return ol.tmoChan
@@ -80,6 +82,7 @@ func (ol *Listener) Close() {
 	close(ol.RspChan)
 	close(ol.ErrChan)
 	close(ol.tmoChan)
+	ol.tmoChan = nil
 }
 
 // The dispatcher is the owner of the listeners it points to.  Only the
diff --git a/nmxact/nmcoap/nmcoap.go b/nmxact/nmcoap/nmcoap.go
index bcf14f3..117f64d 100644
--- a/nmxact/nmcoap/nmcoap.go
+++ b/nmxact/nmcoap/nmcoap.go
@@ -64,7 +64,7 @@ func Encode(m coap.Message) ([]byte, error) {
 	return b, nil
 }
 
-func CreateGet(isTcp bool, resUri string, token []byte) (coap.Message, error) {
+func CreateGet(isTcp bool, resUri string, observe int, token []byte) (coap.Message, error) {
 	var q []string
 
 	if err := validateToken(token); err != nil {
@@ -85,6 +85,10 @@ func CreateGet(isTcp bool, resUri string, token []byte) (coap.Message, error) {
 		m.SetURIQuery(q[1])
 	}
 
+	if observe >= 0 {
+		m.SetObserve(observe)
+	}
+
 	return m, nil
 }
 
diff --git a/nmxact/nmserial/serial_sesn.go b/nmxact/nmserial/serial_sesn.go
index e8bbc2e..85b6b16 100644
--- a/nmxact/nmserial/serial_sesn.go
+++ b/nmxact/nmserial/serial_sesn.go
@@ -169,6 +169,11 @@ func (s *SerialSesn) TxCoapOnce(m coap.Message, resType sesn.ResourceType,
 	}
 }
 
+func (s *SerialSesn) TxCoapObserve(m coap.Message, resType sesn.ResourceType,
+	opt sesn.TxOptions, NotifCb sesn.GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+	return 0, nil, nil, nil
+}
+
 func (s *SerialSesn) MgmtProto() sesn.MgmtProto {
 	return s.cfg.MgmtProto
 }
diff --git a/nmxact/sesn/sesn.go b/nmxact/sesn/sesn.go
index fefa02a..ad4f5b9 100644
--- a/nmxact/sesn/sesn.go
+++ b/nmxact/sesn/sesn.go
@@ -27,6 +27,8 @@ import (
 	"mynewt.apache.org/newtmgr/nmxact/nmp"
 )
 
+type GetNotifyCb func(path string, code coap.COAPCode, value []byte, token []byte)
+
 type TxOptions struct {
 	Timeout time.Duration
 	Tries   int
@@ -99,4 +101,7 @@ type Sesn interface {
 
 	TxCoapOnce(m coap.Message, resType ResourceType,
 		opt TxOptions) (coap.COAPCode, []byte, error)
+
+	TxCoapObserve(m coap.Message, resType ResourceType,
+		opt TxOptions, NotifCb GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error)
 }
diff --git a/nmxact/sesn/sesn_util.go b/nmxact/sesn/sesn_util.go
index 7476451..4bf51dd 100644
--- a/nmxact/sesn/sesn_util.go
+++ b/nmxact/sesn/sesn_util.go
@@ -44,7 +44,7 @@ func TxNmp(s Sesn, m *nmp.NmpMsg, o TxOptions) (nmp.NmpRsp, error) {
 func getResourceOnce(s Sesn, resType ResourceType,
 	uri string, opt TxOptions) (coap.COAPCode, []byte, error) {
 
-	req, err := nmcoap.CreateGet(s.CoapIsTcp(), uri, nmxutil.NextToken())
+	req, err := nmcoap.CreateGet(s.CoapIsTcp(), uri, -1, nmxutil.NextToken())
 	if err != nil {
 		return 0, nil, err
 	}
@@ -52,6 +52,29 @@ func getResourceOnce(s Sesn, resType ResourceType,
 	return s.TxCoapOnce(req, resType, opt)
 }
 
+func getResource(s Sesn, resType ResourceType,
+	uri string, observe int, token []byte, opt TxOptions, NotifCb GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+
+	var req coap.Message
+	var err error
+
+	if observe == 1 {
+		req, err = nmcoap.CreateGet(s.CoapIsTcp(), uri, observe, token)
+	} else {
+		req, err = nmcoap.CreateGet(s.CoapIsTcp(), uri, observe, nmxutil.NextToken())
+	}
+	if err != nil {
+		return 0, nil, nil, err
+	}
+
+	if observe == 0 {
+		return s.TxCoapObserve(req, resType, opt, NotifCb, stopsignal)
+	}
+
+	c, r, err := s.TxCoapOnce(req, resType, opt)
+	return c, r, nil, err
+}
+
 func putResourceOnce(s Sesn, resType ResourceType,
 	uri string, value []byte,
 	opt TxOptions) (coap.COAPCode, []byte, error) {
@@ -105,6 +128,31 @@ func txCoap(txCb func() (coap.COAPCode, []byte, error),
 	}
 }
 
+func txCoapObserve(txCb func() (coap.COAPCode, []byte, []byte, error),
+	tries int) (coap.COAPCode, []byte, []byte, error) {
+
+	retries := tries - 1
+	for i := 0; ; i++ {
+		code, r, t, err := txCb()
+		if err == nil {
+			return code, r, t, nil
+		}
+
+		if !nmxutil.IsRspTimeout(err) || i >= retries {
+			return code, nil, nil, err
+		}
+	}
+}
+
+func GetResourceObserve(s Sesn, resType ResourceType, uri string, o TxOptions, notifCb GetNotifyCb,
+	stopsignal chan int, observe int, token []byte) (
+	coap.COAPCode, []byte, []byte, error) {
+
+	return txCoapObserve(func() (coap.COAPCode, []byte, []byte, error) {
+		return getResource(s, resType, uri, observe, token, o, notifCb, stopsignal)
+	}, o.Tries)
+}
+
 func GetResource(s Sesn, resType ResourceType, uri string, o TxOptions) (
 	coap.COAPCode, []byte, error) {
 
diff --git a/nmxact/udp/udp_sesn.go b/nmxact/udp/udp_sesn.go
index 5e5a0d5..c443032 100644
--- a/nmxact/udp/udp_sesn.go
+++ b/nmxact/udp/udp_sesn.go
@@ -142,6 +142,24 @@ func (s *UdpSesn) MgmtProto() sesn.MgmtProto {
 	return s.cfg.MgmtProto
 }
 
+func (s *UdpSesn) TxCoapObserve(m coap.Message, resType sesn.ResourceType,
+	opt sesn.TxOptions, NotifyCb sesn.GetNotifyCb, stopsignal chan int) (coap.COAPCode, []byte, []byte, error) {
+
+	txRaw := func(b []byte) error {
+		_, err := s.conn.WriteToUDP(b, s.addr)
+		return err
+	}
+
+	rsp, err := s.txvr.TxOicObserve(txRaw, m, s.MtuOut(), opt.Timeout, NotifyCb, stopsignal)
+	if err != nil {
+		return 0, nil, nil, err
+	} else if rsp == nil {
+		return 0, nil, nil, nil
+	} else {
+		return rsp.Code(), rsp.Payload(), rsp.Token(), nil
+	}
+}
+
 func (s *UdpSesn) CoapIsTcp() bool {
 	return false
 }
diff --git a/nmxact/xact/res.go b/nmxact/xact/res.go
index f54614a..63b1cd5 100644
--- a/nmxact/xact/res.go
+++ b/nmxact/xact/res.go
@@ -27,8 +27,12 @@ import (
 
 type GetResCmd struct {
 	CmdBase
-	Path string
-	Typ  sesn.ResourceType
+	Path       string
+	Observe    int
+	NotifyFunc sesn.GetNotifyCb
+	StopSignal chan int
+	Token      []byte
+	Typ        sesn.ResourceType
 }
 
 func NewGetResCmd() *GetResCmd {
@@ -40,6 +44,7 @@ func NewGetResCmd() *GetResCmd {
 type GetResResult struct {
 	Code  coap.COAPCode
 	Value []byte
+	Token []byte
 }
 
 func newGetResResult() *GetResResult {
@@ -55,7 +60,18 @@ func (r *GetResResult) Status() int {
 }
 
 func (c *GetResCmd) Run(s sesn.Sesn) (Result, error) {
-	status, val, err := sesn.GetResource(s, c.Typ, c.Path, c.TxOptions())
+	var status coap.COAPCode
+	var val []byte
+	var token []byte
+	var err error
+
+	if c.Observe != -1 {
+		status, val, token, err = sesn.GetResourceObserve(s, c.Typ, c.Path, c.TxOptions(),
+			c.NotifyFunc, c.StopSignal, c.Observe, c.Token)
+	} else {
+		status, val, err = sesn.GetResource(s, c.Typ, c.Path, c.TxOptions())
+	}
+
 	if err != nil {
 		return nil, err
 	}
@@ -63,6 +79,8 @@ func (c *GetResCmd) Run(s sesn.Sesn) (Result, error) {
 	res := newGetResResult()
 	res.Code = status
 	res.Value = val
+	res.Token = token
+
 	return res, nil
 }
 
diff --git a/vendor/github.com/flynn/go-shlex/COPYING b/vendor/github.com/flynn/go-shlex/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/flynn/go-shlex/COPYING
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/github.com/flynn/go-shlex/Makefile b/vendor/github.com/flynn/go-shlex/Makefile
new file mode 100644
index 0000000..038d9a4
--- /dev/null
+++ b/vendor/github.com/flynn/go-shlex/Makefile
@@ -0,0 +1,21 @@
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include $(GOROOT)/src/Make.inc
+
+TARG=shlex
+GOFILES=\
+	shlex.go\
+
+include $(GOROOT)/src/Make.pkg
diff --git a/vendor/github.com/flynn/go-shlex/README.md b/vendor/github.com/flynn/go-shlex/README.md
new file mode 100644
index 0000000..c86bcc0
--- /dev/null
+++ b/vendor/github.com/flynn/go-shlex/README.md
@@ -0,0 +1,2 @@
+go-shlex is a simple lexer for go that supports shell-style quoting,
+commenting, and escaping.
diff --git a/vendor/github.com/flynn/go-shlex/shlex.go b/vendor/github.com/flynn/go-shlex/shlex.go
new file mode 100644
index 0000000..7aeace8
--- /dev/null
+++ b/vendor/github.com/flynn/go-shlex/shlex.go
@@ -0,0 +1,457 @@
+/*
+Copyright 2012 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package shlex
+
+/*
+Package shlex implements a simple lexer which splits input in to tokens using
+shell-style rules for quoting and commenting.
+*/
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"io"
+	"strings"
+)
+
+/*
+A TokenType is a top-level token; a word, space, comment, unknown.
+*/
+type TokenType int
+
+/*
+A RuneTokenType is the type of a UTF-8 character; a character, quote, space, escape.
+*/
+type RuneTokenType int
+
+type lexerState int
+
+type Token struct {
+	tokenType TokenType
+	value     string
+}
+
+/*
+Two tokens are equal if both their types and values are equal. A nil token can
+never equal another token.
+*/
+func (a *Token) Equal(b *Token) bool {
+	if a == nil || b == nil {
+		return false
+	}
+	if a.tokenType != b.tokenType {
+		return false
+	}
+	return a.value == b.value
+}
+
+const (
+	RUNE_CHAR              string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-,/@$*()+=><:;&^%~|!?[]{}"
+	RUNE_SPACE             string = " \t\r\n"
+	RUNE_ESCAPING_QUOTE    string = "\""
+	RUNE_NONESCAPING_QUOTE string = "'"
+	RUNE_ESCAPE                   = "\\"
+	RUNE_COMMENT                  = "#"
+
+	RUNETOKEN_UNKNOWN           RuneTokenType = 0
+	RUNETOKEN_CHAR              RuneTokenType = 1
+	RUNETOKEN_SPACE             RuneTokenType = 2
+	RUNETOKEN_ESCAPING_QUOTE    RuneTokenType = 3
+	RUNETOKEN_NONESCAPING_QUOTE RuneTokenType = 4
+	RUNETOKEN_ESCAPE            RuneTokenType = 5
+	RUNETOKEN_COMMENT           RuneTokenType = 6
+	RUNETOKEN_EOF               RuneTokenType = 7
+
+	TOKEN_UNKNOWN TokenType = 0
+	TOKEN_WORD    TokenType = 1
+	TOKEN_SPACE   TokenType = 2
+	TOKEN_COMMENT TokenType = 3
+
+	STATE_START           lexerState = 0
+	STATE_INWORD          lexerState = 1
+	STATE_ESCAPING        lexerState = 2
+	STATE_ESCAPING_QUOTED lexerState = 3
+	STATE_QUOTED_ESCAPING lexerState = 4
+	STATE_QUOTED          lexerState = 5
+	STATE_COMMENT         lexerState = 6
+
+	INITIAL_TOKEN_CAPACITY int = 100
+)
+
+/*
+A type for classifying characters. This allows for different sorts of
+classifiers - those accepting extended non-ascii chars, or strict posix
+compatibility, for example.
+*/
+type TokenClassifier struct {
+	typeMap map[int32]RuneTokenType
+}
+
+func addRuneClass(typeMap *map[int32]RuneTokenType, runes string, tokenType RuneTokenType) {
+	for _, rune := range runes {
+		(*typeMap)[int32(rune)] = tokenType
+	}
+}
+
+/*
+Create a new classifier for basic ASCII characters.
+*/
+func NewDefaultClassifier() *TokenClassifier {
+	typeMap := map[int32]RuneTokenType{}
+	addRuneClass(&typeMap, RUNE_CHAR, RUNETOKEN_CHAR)
+	addRuneClass(&typeMap, RUNE_SPACE, RUNETOKEN_SPACE)
+	addRuneClass(&typeMap, RUNE_ESCAPING_QUOTE, RUNETOKEN_ESCAPING_QUOTE)
+	addRuneClass(&typeMap, RUNE_NONESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE)
+	addRuneClass(&typeMap, RUNE_ESCAPE, RUNETOKEN_ESCAPE)
+	addRuneClass(&typeMap, RUNE_COMMENT, RUNETOKEN_COMMENT)
+	return &TokenClassifier{
+		typeMap: typeMap}
+}
+
+func (classifier *TokenClassifier) ClassifyRune(rune int32) RuneTokenType {
+	return classifier.typeMap[rune]
+}
+
+/*
+A type for turning an input stream in to a sequence of strings. Whitespace and
+comments are skipped.
+*/
+type Lexer struct {
+	tokenizer *Tokenizer
+}
+
+/*
+Create a new lexer.
+*/
+func NewLexer(r io.Reader) (*Lexer, error) {
+
+	tokenizer, err := NewTokenizer(r)
+	if err != nil {
+		return nil, err
+	}
+	lexer := &Lexer{tokenizer: tokenizer}
+	return lexer, nil
+}
+
+/*
+Return the next word, and an error value. If there are no more words, the error
+will be io.EOF.
+*/
+func (l *Lexer) NextWord() (string, error) {
+	var token *Token
+	var err error
+	for {
+		token, err = l.tokenizer.NextToken()
+		if err != nil {
+			return "", err
+		}
+		switch token.tokenType {
+		case TOKEN_WORD:
+			{
+				return token.value, nil
+			}
+		case TOKEN_COMMENT:
+			{
+				// skip comments
+			}
+		default:
+			{
+				panic(fmt.Sprintf("Unknown token type: %v", token.tokenType))
+			}
+		}
+	}
+	return "", io.EOF
+}
+
+/*
+A type for turning an input stream in to a sequence of typed tokens.
+*/
+type Tokenizer struct {
+	input      *bufio.Reader
+	classifier *TokenClassifier
+}
+
+/*
+Create a new tokenizer.
+*/
+func NewTokenizer(r io.Reader) (*Tokenizer, error) {
+	input := bufio.NewReader(r)
+	classifier := NewDefaultClassifier()
+	tokenizer := &Tokenizer{
+		input:      input,
+		classifier: classifier}
+	return tokenizer, nil
+}
+
+/*
+Scan the stream for the next token.
+
+This uses an internal state machine. It will panic if it encounters a character
+which it does not know how to handle.
+*/
+func (t *Tokenizer) scanStream() (*Token, error) {
+	state := STATE_START
+	var tokenType TokenType
+	value := make([]int32, 0, INITIAL_TOKEN_CAPACITY)
+	var (
+		nextRune     int32
+		nextRuneType RuneTokenType
+		err          error
+	)
+SCAN:
+	for {
+		nextRune, _, err = t.input.ReadRune()
+		nextRuneType = t.classifier.ClassifyRune(nextRune)
+		if err != nil {
+			if err == io.EOF {
+				nextRuneType = RUNETOKEN_EOF
+				err = nil
+			} else {
+				return nil, err
+			}
+		}
+		switch state {
+		case STATE_START: // no runes read yet
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						return nil, io.EOF
+					}
+				case RUNETOKEN_CHAR:
+					{
+						tokenType = TOKEN_WORD
+						value = append(value, nextRune)
+						state = STATE_INWORD
+					}
+				case RUNETOKEN_SPACE:
+					{
+					}
+				case RUNETOKEN_ESCAPING_QUOTE:
+					{
+						tokenType = TOKEN_WORD
+						state = STATE_QUOTED_ESCAPING
+					}
+				case RUNETOKEN_NONESCAPING_QUOTE:
+					{
+						tokenType = TOKEN_WORD
+						state = STATE_QUOTED
+					}
+				case RUNETOKEN_ESCAPE:
+					{
+						tokenType = TOKEN_WORD
+						state = STATE_ESCAPING
+					}
+				case RUNETOKEN_COMMENT:
+					{
+						tokenType = TOKEN_COMMENT
+						state = STATE_COMMENT
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Unknown rune: %v", nextRune))
+					}
+				}
+			}
+		case STATE_INWORD: // in a regular word
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						break SCAN
+					}
+				case RUNETOKEN_CHAR, RUNETOKEN_COMMENT:
+					{
+						value = append(value, nextRune)
+					}
+				case RUNETOKEN_SPACE:
+					{
+						t.input.UnreadRune()
+						break SCAN
+					}
+				case RUNETOKEN_ESCAPING_QUOTE:
+					{
+						state = STATE_QUOTED_ESCAPING
+					}
+				case RUNETOKEN_NONESCAPING_QUOTE:
+					{
+						state = STATE_QUOTED
+					}
+				case RUNETOKEN_ESCAPE:
+					{
+						state = STATE_ESCAPING
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune))
+					}
+				}
+			}
+		case STATE_ESCAPING: // the next rune after an escape character
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						err = errors.New("EOF found after escape character")
+						break SCAN
+					}
+				case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT:
+					{
+						state = STATE_INWORD
+						value = append(value, nextRune)
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune))
+					}
+				}
+			}
+		case STATE_ESCAPING_QUOTED: // the next rune after an escape character, in double quotes
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						err = errors.New("EOF found after escape character")
+						break SCAN
+					}
+				case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT:
+					{
+						state = STATE_QUOTED_ESCAPING
+						value = append(value, nextRune)
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune))
+					}
+				}
+			}
+		case STATE_QUOTED_ESCAPING: // in escaping double quotes
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						err = errors.New("EOF found when expecting closing quote.")
+						break SCAN
+					}
+				case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_COMMENT:
+					{
+						value = append(value, nextRune)
+					}
+				case RUNETOKEN_ESCAPING_QUOTE:
+					{
+						state = STATE_INWORD
+					}
+				case RUNETOKEN_ESCAPE:
+					{
+						state = STATE_ESCAPING_QUOTED
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune))
+					}
+				}
+			}
+		case STATE_QUOTED: // in non-escaping single quotes
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						err = errors.New("EOF found when expecting closing quote.")
+						break SCAN
+					}
+				case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT:
+					{
+						value = append(value, nextRune)
+					}
+				case RUNETOKEN_NONESCAPING_QUOTE:
+					{
+						state = STATE_INWORD
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune))
+					}
+				}
+			}
+		case STATE_COMMENT:
+			{
+				switch nextRuneType {
+				case RUNETOKEN_EOF:
+					{
+						break SCAN
+					}
+				case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT, RUNETOKEN_NONESCAPING_QUOTE:
+					{
+						value = append(value, nextRune)
+					}
+				case RUNETOKEN_SPACE:
+					{
+						if nextRune == '\n' {
+							state = STATE_START
+							break SCAN
+						} else {
+							value = append(value, nextRune)
+						}
+					}
+				default:
+					{
+						return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune))
+					}
+				}
+			}
+		default:
+			{
+				panic(fmt.Sprintf("Unexpected state: %v", state))
+			}
+		}
+	}
+	token := &Token{
+		tokenType: tokenType,
+		value:     string(value)}
+	return token, err
+}
+
+/*
+Return the next token in the stream, and an error value. If there are no more
+tokens available, the error value will be io.EOF.
+*/
+func (t *Tokenizer) NextToken() (*Token, error) {
+	return t.scanStream()
+}
+
+/*
+Split a string in to a slice of strings, based upon shell-style rules for
+quoting, escaping, and spaces.
+*/
+func Split(s string) ([]string, error) {
+	l, err := NewLexer(strings.NewReader(s))
+	if err != nil {
+		return nil, err
+	}
+	subStrings := []string{}
+	for {
+		word, err := l.NextWord()
+		if err != nil {
+			if err == io.EOF {
+				return subStrings, nil
+			}
+			return subStrings, err
+		}
+		subStrings = append(subStrings, word)
+	}
+	return subStrings, nil
+}
diff --git a/vendor/github.com/flynn/go-shlex/shlex_test.go b/vendor/github.com/flynn/go-shlex/shlex_test.go
new file mode 100644
index 0000000..7551f7c
--- /dev/null
+++ b/vendor/github.com/flynn/go-shlex/shlex_test.go
@@ -0,0 +1,162 @@
+/*
+Copyright 2012 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package shlex
+
+import (
+	"strings"
+	"testing"
+)
+
+func checkError(err error, t *testing.T) {
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func TestClassifier(t *testing.T) {
+	classifier := NewDefaultClassifier()
+	runeTests := map[int32]RuneTokenType{
+		'a':  RUNETOKEN_CHAR,
+		' ':  RUNETOKEN_SPACE,
+		'"':  RUNETOKEN_ESCAPING_QUOTE,
+		'\'': RUNETOKEN_NONESCAPING_QUOTE,
+		'#':  RUNETOKEN_COMMENT}
+	for rune, expectedType := range runeTests {
+		foundType := classifier.ClassifyRune(rune)
+		if foundType != expectedType {
+			t.Logf("Expected type: %v for rune '%c'(%v). Found type: %v.", expectedType, rune, rune, foundType)
+			t.Fail()
+		}
+	}
+}
+
+func TestTokenizer(t *testing.T) {
+	testInput := strings.NewReader("one two \"three four\" \"five \\\"six\\\"\" seven#eight # nine # ten\n eleven")
+	expectedTokens := []*Token{
+		&Token{
+			tokenType: TOKEN_WORD,
+			value:     "one"},
+		&Token{
+			tokenType: TOKEN_WORD,
+			value:     "two"},
+		&Token{
+			tokenType: TOKEN_WORD,
+			value:     "three four"},
+		&Token{
+			tokenType: TOKEN_WORD,
+			value:     "five \"six\""},
+		&Token{
+			tokenType: TOKEN_WORD,
+			value:     "seven#eight"},
+		&Token{
+			tokenType: TOKEN_COMMENT,
+			value:     " nine # ten"},
+		&Token{
+			tokenType: TOKEN_WORD,
+			value:     "eleven"}}
+
+	tokenizer, err := NewTokenizer(testInput)
+	checkError(err, t)
+	for _, expectedToken := range expectedTokens {
+		foundToken, err := tokenizer.NextToken()
+		checkError(err, t)
+		if !foundToken.Equal(expectedToken) {
+			t.Error("Expected token:", expectedToken, ". Found:", foundToken)
+		}
+	}
+}
+
+func TestLexer(t *testing.T) {
+	testInput := strings.NewReader("one")
+	expectedWord := "one"
+	lexer, err := NewLexer(testInput)
+	checkError(err, t)
+	foundWord, err := lexer.NextWord()
+	checkError(err, t)
+	if expectedWord != foundWord {
+		t.Error("Expected word:", expectedWord, ". Found:", foundWord)
+	}
+}
+
+func TestSplitSimple(t *testing.T) {
+	testInput := "one two three"
+	expectedOutput := []string{"one", "two", "three"}
+	foundOutput, err := Split(testInput)
+	if err != nil {
+		t.Error("Split returned error:", err)
+	}
+	if len(expectedOutput) != len(foundOutput) {
+		t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results")
+	}
+	for i := range foundOutput {
+		if foundOutput[i] != expectedOutput[i] {
+			t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i])
+		}
+	}
+}
+
+func TestSplitEscapingQuotes(t *testing.T) {
+	testInput := "one \"??? ${three}\" four"
+	expectedOutput := []string{"one", "??? ${three}", "four"}
+	foundOutput, err := Split(testInput)
+	if err != nil {
+		t.Error("Split returned error:", err)
+	}
+	if len(expectedOutput) != len(foundOutput) {
+		t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results")
+	}
+	for i := range foundOutput {
+		if foundOutput[i] != expectedOutput[i] {
+			t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i])
+		}
+	}
+}
+
+func TestGlobbingExpressions(t *testing.T) {
+	testInput := "onefile *file one?ile onefil[de]"
+	expectedOutput := []string{"onefile", "*file", "one?ile", "onefil[de]"}
+	foundOutput, err := Split(testInput)
+	if err != nil {
+		t.Error("Split returned error", err)
+	}
+	if len(expectedOutput) != len(foundOutput) {
+		t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results")
+	}
+	for i := range foundOutput {
+		if foundOutput[i] != expectedOutput[i] {
+			t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i])
+		}
+	}
+
+}
+
+func TestSplitNonEscapingQuotes(t *testing.T) {
+	testInput := "one '??? ${three}' four"
+	expectedOutput := []string{"one", "??? ${three}", "four"}
+	foundOutput, err := Split(testInput)
+	if err != nil {
+		t.Error("Split returned error:", err)
+	}
+	if len(expectedOutput) != len(foundOutput) {
+		t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results")
+	}
+	for i := range foundOutput {
+		if foundOutput[i] != expectedOutput[i] {
+			t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i])
+		}
+	}
+}
diff --git a/vendor/github.com/runtimeco/go-coap/message.go b/vendor/github.com/runtimeco/go-coap/message.go
index c80841b..c20333e 100644
--- a/vendor/github.com/runtimeco/go-coap/message.go
+++ b/vendor/github.com/runtimeco/go-coap/message.go
@@ -350,6 +350,7 @@ type Message interface {
 	SetPathString(s string)
 	SetPath(s []string)
 	SetURIQuery(s string)
+	SetObserve(b int)
 	SetPayload(p []byte)
 	RemoveOption(opID OptionID)
 	AddOption(opID OptionID, val interface{})
@@ -465,6 +466,11 @@ func (m *MessageBase) SetURIQuery(s string) {
 	m.AddOption(URIQuery, s)
 }
 
+// Set Observer attribute to the message
+func (m *MessageBase) SetObserve(b int) {
+	m.AddOption(Observe, b)
+}
+
 // SetPayload
 func (m *MessageBase) SetPayload(p []byte) {
 	m.payload = p
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/.gitignore b/vendor/gopkg.in/abiosoft/ishell.v1/.gitignore
new file mode 100644
index 0000000..e55627b
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/.gitignore
@@ -0,0 +1,5 @@
+*~
+.DS_Store
+example/example
+.idea/
+*.iml
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/.travis.yml b/vendor/gopkg.in/abiosoft/ishell.v1/.travis.yml
new file mode 100644
index 0000000..c9bd5fe
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/.travis.yml
@@ -0,0 +1,9 @@
+language: go
+script:
+  - go vet
+  - go test -v ./...
+  - go build
+go:
+  - 1.6.x
+  - 1.7.x
+  - 1.8.x
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/CHANGES.md b/vendor/gopkg.in/abiosoft/ishell.v1/CHANGES.md
new file mode 100644
index 0000000..bd20ae0
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/CHANGES.md
@@ -0,0 +1,32 @@
+For now, dates (DD/MM/YYYY) are used until ishell gets stable enough to warrant tags.
+Attempts will be made to ensure non breaking updates as much as possible.
+#### 28/05/2017
+* Added `shell.Process(os.Args[1:]...)` for non-interactive execution
+*
+
+
+#### 07/02/2016
+Added multiline support to shell mode.
+
+#### 23/01/2016
+* Added history support.
+* Added tab completion support.
+* Added `SetHistoryPath`, `SetMultiPrompt`
+* Removed password masks.
+* **Breaking Change**: changed definition of `ReadPassword` from `(string)` to `()`
+* **Breaking Change**: changed name of `Shell` constructor from `NewShell` to `New`
+
+#### 13/07/2015
+* Added `ClearScreen` method.
+* Added `clear` to default commands.
+
+#### 12/07/2015:
+* Added `PrintCommands`, `Commands` and `ShowPrompt` methods.
+* Added default `exit` and `help` commands.
+* **Breaking Change**: changed return values of `ReadLine` from `(string, error)` to `string.`
+* **Breaking Change**: changed definition of `CmdFunc` from `(cmd string, args []string)` to `(args ...String)` to remove redundant command being passed.
+* Added multiline input support.
+* Added case insensitive command support.
+
+#### 11/07/2015:
+* Initial version.
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/LICENSE b/vendor/gopkg.in/abiosoft/ishell.v1/LICENSE
new file mode 100644
index 0000000..a83a12f
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Abiola Ibrahim
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/README.md b/vendor/gopkg.in/abiosoft/ishell.v1/README.md
new file mode 100644
index 0000000..41e5f89
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/README.md
@@ -0,0 +1,322 @@
+# ishell
+ishell is an interactive shell library for creating interactive cli applications.
+
+[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/abiosoft/ishell)
+[![Go Report Card](https://goreportcard.com/badge/github.com/abiosoft/ishell)](https://goreportcard.com/report/github.com/abiosoft/ishell)
+
+## Older version
+The current master is not backward compatible with older version. Kindly change your import path to `gopkg.in/abiosoft/ishell.v1`.
+
+Older version of this library is still available at [https://gopkg.in/abiosoft/ishell.v1](https://gopkg.in/abiosoft/ishell.v1).
+
+However, you are advised to upgrade.
+
+## Usage
+
+```go
+import "strings"
+import "github.com/abiosoft/ishell"
+
+func main(){
+    // create new shell.
+    // by default, new shell includes 'exit', 'help' and 'clear' commands.
+    shell := ishell.New()
+
+    // display welcome info.
+    shell.Println("Sample Interactive Shell")
+
+    // register a function for "greet" command.
+    shell.AddCmd(&ishell.Cmd{
+        Name: "greet",
+        Help: "greet user",
+        Func: func(c *ishell.Context) {
+            c.Println("Hello", strings.Join(c.Args, " "))
+        },
+    })
+
+    // run shell
+    shell.Run()
+}
+```
+Execution
+```
+Sample Interactive Shell
+>>> help
+
+Commands:
+  clear      clear the screen
+  greet      greet user
+  exit       exit the program
+  help       display help
+
+>>> greet Someone Somewhere
+Hello Someone Somewhere
+>>> exit
+$
+```
+
+### Reading input
+```go
+// simulate an authentication
+shell.AddCmd(&ishell.Cmd{
+    Name: "login",
+    Help: "simulate a login",
+    Func: func(c *ishell.Context) {
+        // disable the '>>>' for cleaner same line input.
+        c.ShowPrompt(false)
+        defer c.ShowPrompt(true) // yes, revert after login.
+
+        // get username
+        c.Print("Username: ")
+        username := c.ReadLine()
+
+        // get password.
+        c.Print("Password: ")
+        password := c.ReadPassword()
+
+        ... // do something with username and password
+
+        c.Println("Authentication Successful.")
+    },
+})
+```
+Execution
+```
+>>> login
+Username: someusername
+Password:
+Authentication Successful.
+```
+
+### Multiline input
+Builtin support for multiple lines.
+```
+>>> This is \
+... multi line
+
+>>> Cool that << EOF
+... everything here goes
+... as a single argument.
+... EOF
+```
+User defined
+```go
+shell.AddCmd(&ishell.Cmd{
+    Name: "multi",
+    Help: "input in multiple lines",
+    Func: func(c *ishell.Context) {
+        c.Println("Input multiple lines and end with semicolon ';'.")
+        lines := c.ReadMultiLines(";")
+        c.Println("Done reading. You wrote:")
+        c.Println(lines)
+    },
+})
+```
+Execution
+```
+>>> multi
+Input multiple lines and end with semicolon ';'.
+>>> this is user defined
+... multiline input;
+You wrote:
+this is user defined
+multiline input;
+```
+### Keyboard interrupt
+Builtin interrupt handler.
+```
+>>> ^C
+Input Ctrl-C once more to exit
+>>> ^C
+Interrupted
+exit status 1
+```
+Custom
+```go
+shell.Interrupt(func(count int, c *ishell.Context) { ... })
+```
+
+### Multiple Choice
+
+```go
+func(c *ishell.Context) {
+    choice := c.MultiChoice([]string{
+        "Golangers",
+        "Go programmers",
+        "Gophers",
+        "Goers",
+    }, "What are Go programmers called ?")
+    if choice == 2 {
+        c.Println("You got it!")
+    } else {
+        c.Println("Sorry, you're wrong.")
+    }
+},
+```
+Output
+```
+What are Go programmers called ?
+  Golangers
+  Go programmers
+> Gophers
+  Goers
+
+You got it!
+```
+### Checklist
+```go
+func(c *ishell.Context) {
+    languages := []string{"Python", "Go", "Haskell", "Rust"}
+    choices := c.Checklist(languages,
+        "What are your favourite programming languages ?", nil)
+    out := func() []string { ... } // convert index to language
+    c.Println("Your choices are", strings.Join(out(), ", "))
+}
+```
+Output
+```
+What are your favourite programming languages ?
+    Python
+  ? Go
+    Haskell
+ >? Rust
+
+Your choices are Go, Rust
+```
+
+### Progress Bar
+Determinate
+```go
+func(c *ishell.Context) {
+    c.ProgressBar().Start()
+    for i := 0; i < 101; i++ {
+        c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
+        c.ProgressBar().Progress(i)
+        ... // some background computation
+    }
+    c.ProgressBar().Stop()
+}
+```
+Output
+```
+[==========>         ] 50%
+```
+
+Indeterminate
+```go
+
+func(c *ishell.Context) {
+    c.ProgressBar().Indeterminate(true)
+    c.ProgressBar().Start()
+    ... // some background computation
+    c.ProgressBar().Stop()
+}
+```
+Output
+```
+[ ====               ]
+```
+
+Custom display using [briandowns/spinner](https://github.com/briandowns/spinner).
+```go
+display := ishell.ProgressDisplayCharSet(spinner.CharSets[11])
+func(c *Context) { c.ProgressBar().Display(display) ... }
+
+// or set it globally
+ishell.ProgressBar().Display(display)
+```
+
+### Durable history
+```go
+// Read and write history to $HOME/.ishell_history
+shell.SetHomeHistoryPath(".ishell_history")
+```
+
+
+### Non-interactive execution
+In some situations it is desired to exit the program directly after executing a single command.
+
+```go
+// when started with "exit" as first argument, assume non-interactive execution
+if len(os.Args) > 1 && os.Args[1] == "exit" {
+    shell.Process(os.Args[2:]...)
+} else {
+    // start shell
+    shell.Run()
+}
+```
+
+```bash
+# Run normally - interactive mode:
+$ go run main.go
+>>> |
+
+# Run non-interactivelly
+$ go run main.go exit greet Someusername
+Hello Someusername
+```
+
+
+### Output with Color
+You can use [fatih/color](https://github.com/fatih/color).
+
+```go
+func(c *ishell.Context) {
+    yellow := color.New(color.FgYellow).SprintFunc()
+    c.Println(yellow("This line is yellow"))
+}
+```
+Execution
+```sh
+>>> color
+This line is yellow
+```
+
+
+### Example
+Available [here](https://github.com/abiosoft/ishell/blob/master/example/main.go).
+```sh
+go run example/main.go
+```
+
+## Supported Platforms
+* [x] Linux
+* [x] OSX
+* [x] Windows [Not tested but should work]
+
+## Note
+ishell is in active development and can still change significantly.
+
+## Roadmap (in no particular order)
+* [x] Multiline inputs
+* [x] Command history
+* [x] Customizable tab completion
+* [x] Handle ^C interrupts
+* [x] Subcommands and help texts
+* [x] Scrollable paged output
+* [x] Progress bar
+* [x] Multiple choice prompt
+* [x] Checklist prompt
+* [x] Support for command aliases
+* [ ] Multiple line progress bars
+* [ ] Testing, testing, testing
+
+## Contribution
+1. Create an issue to discuss it.
+2. Send in Pull Request.
+
+## License
+MIT
+
+## Credits
+Library | Use
+------- | -----
+[github.com/flynn-archive/go-shlex](https://github.com/flynn-archive/go-shlex) | splitting input into command and args.
+[github.com/chzyer/readline](https://github.com/chzyer/readline) | readline capabilities.
+
+
+## Donate
+```
+bitcoin: 1GTHYEDiy2C7RzXn5nY4wVRaEN2GvLjwZN
+paypal: a@abiosoft.com
+```
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/actions.go b/vendor/gopkg.in/abiosoft/ishell.v1/actions.go
new file mode 100644
index 0000000..8b223e4
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/actions.go
@@ -0,0 +1,175 @@
+package ishell
+
+import (
+	"bytes"
+	"fmt"
+	"os/exec"
+	"runtime"
+	"strings"
+)
+
+// Actions are actions that can be performed by a shell.
+type Actions interface {
+	// ReadLine reads a line from standard input.
+	ReadLine() string
+	// ReadLineErr is ReadLine but returns error as well
+	ReadLineErr() (string, error)
+	// ReadPassword reads password from standard input without echoing the characters.
+	// Note that this only works as expected when the standard input is a terminal.
+	ReadPassword() string
+	// ReadPasswordErr is ReadPassword but returns error as well
+	ReadPasswordErr() (string, error)
+	// ReadMultiLinesFunc reads multiple lines from standard input. It passes each read line to
+	// f and stops reading when f returns false.
+	ReadMultiLinesFunc(f func(string) bool) string
+	// ReadMultiLines reads multiple lines from standard input. It stops reading when terminator
+	// is encountered at the end of the line. It returns the lines read including terminator.
+	// For more control, use ReadMultiLinesFunc.
+	ReadMultiLines(terminator string) string
+	// Println prints to output and ends with newline character.
+	Println(val ...interface{})
+	// Print prints to output.
+	Print(val ...interface{})
+	// Printf prints to output using string format.
+	Printf(format string, val ...interface{})
+	// ShowPaged shows a paged text that is scrollable.
+	// This leverages on "less" for unix and "more" for windows.
+	ShowPaged(text string) error
+	// MultiChoice presents options to the user.
+	// returns the index of the selection or -1 if nothing is
+	// selected.
+	// text is displayed before the options.
+	MultiChoice(options []string, text string) int
+	// Checklist is similar to MultiChoice but user can choose multiple variants using Space.
+	// init is initially selected options.
+	Checklist(options []string, text string, init []int) []int
+	// SetPrompt sets the prompt string. The string to be displayed before the cursor.
+	SetPrompt(prompt string)
+	// SetMultiPrompt sets the prompt string used for multiple lines. The string to be displayed before
+	// the cursor; starting from the second line of input.
+	SetMultiPrompt(prompt string)
+	// ShowPrompt sets whether prompt should show when requesting input for ReadLine and ReadPassword.
+	// Defaults to true.
+	ShowPrompt(show bool)
+	// Cmds returns all the commands added to the shell.
+	Cmds() []*Cmd
+	// HelpText returns the computed help of top level commands.
+	HelpText() string
+	// ClearScreen clears the screen. Same behaviour as running 'clear' in unix terminal or 'cls' in windows cmd.
+	ClearScreen() error
+	// Stop stops the shell. This will stop the shell from auto reading inputs and calling
+	// registered functions. A stopped shell is only inactive but totally functional.
+	// Its functions can still be called and can be restarted.
+	Stop()
+}
+
+type shellActionsImpl struct {
+	*Shell
+}
+
+// ReadLine reads a line from standard input.
+func (s *shellActionsImpl) ReadLine() string {
+	line, _ := s.readLine()
+	return line
+}
+
+func (s *shellActionsImpl) ReadLineErr() (string, error) {
+	return s.readLine()
+}
+
+func (s *shellActionsImpl) ReadPassword() string {
+	return s.reader.readPassword()
+}
+
+func (s *shellActionsImpl) ReadPasswordErr() (string, error) {
+	return s.reader.readPasswordErr()
+}
+
+func (s *shellActionsImpl) ReadMultiLinesFunc(f func(string) bool) string {
+	lines, _ := s.readMultiLinesFunc(f)
+	return lines
+}
+
+func (s *shellActionsImpl) ReadMultiLines(terminator string) string {
+	return s.ReadMultiLinesFunc(func(line string) bool {
+		if strings.HasSuffix(strings.TrimSpace(line), terminator) {
+			return false
+		}
+		return true
+	})
+}
+
+func (s *shellActionsImpl) Println(val ...interface{}) {
+	s.reader.buf.Truncate(0)
+	fmt.Fprintln(s.writer, val...)
+}
+
+func (s *shellActionsImpl) Print(val ...interface{}) {
+	s.reader.buf.Truncate(0)
+	fmt.Fprint(s.reader.buf, val...)
+	fmt.Fprint(s.writer, val...)
+}
+
+func (s *shellActionsImpl) Printf(format string, val ...interface{}) {
+	s.reader.buf.Truncate(0)
+	fmt.Fprintf(s.reader.buf, format, val...)
+	fmt.Fprintf(s.writer, format, val...)
+}
+
+func (s *shellActionsImpl) MultiChoice(options []string, text string) int {
+	choice := s.multiChoice(options, text, nil, false)
+	return choice[0]
+}
+func (s *shellActionsImpl) Checklist(options []string, text string, init []int) []int {
+	return s.multiChoice(options, text, init, true)
+}
+func (s *shellActionsImpl) SetPrompt(prompt string) {
+	s.reader.prompt = prompt
+	s.reader.scanner.SetPrompt(s.reader.rlPrompt())
+}
+
+func (s *shellActionsImpl) SetMultiPrompt(prompt string) {
+	s.reader.multiPrompt = prompt
+}
+
+func (s *shellActionsImpl) ShowPrompt(show bool) {
+	s.reader.showPrompt = show
+	s.reader.scanner.SetPrompt(s.reader.rlPrompt())
+}
+
+func (s *shellActionsImpl) Cmds() []*Cmd {
+	var cmds []*Cmd
+	for _, cmd := range s.rootCmd.children {
+		cmds = append(cmds, cmd)
+	}
+	return cmds
+}
+
+func (s *shellActionsImpl) ClearScreen() error {
+	return clearScreen(s.Shell)
+}
+
+func (s *shellActionsImpl) ShowPaged(text string) error {
+	return showPaged(s.Shell, text)
+}
+
+func (s *shellActionsImpl) Stop() {
+	s.stop()
+}
+
+func (s *shellActionsImpl) HelpText() string {
+	return s.rootCmd.HelpText()
+}
+
+func showPaged(s *Shell, text string) error {
+	var cmd *exec.Cmd
+	if runtime.GOOS == "windows" {
+		cmd = exec.Command("more")
+	} else {
+		cmd = exec.Command("less")
+	}
+	cmd.Stdout = s.writer
+	cmd.Stderr = s.writer
+	cmd.Stdin = bytes.NewBufferString(text)
+	return cmd.Run()
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/command.go b/vendor/gopkg.in/abiosoft/ishell.v1/command.go
new file mode 100644
index 0000000..4ede7e6
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/command.go
@@ -0,0 +1,134 @@
+package ishell
+
+import (
+	"bytes"
+	"fmt"
+	"sort"
+	"text/tabwriter"
+)
+
+// Cmd is a shell command handler.
+type Cmd struct {
+	// Command name.
+	Name string
+	// Command name aliases.
+	Aliases []string
+	// Function to execute for the command.
+	Func func(c *Context)
+	// One liner help message for the command.
+	Help string
+	// More descriptive help message for the command.
+	LongHelp string
+
+	// Completer is custom autocomplete for command.
+	// It takes in command arguments and returns
+	// autocomplete options.
+	// By default all commands get autocomplete of
+	// subcommands.
+	// A non-nil Completer overrides the default behaviour.
+	Completer func(args []string) []string
+
+	// subcommands.
+	children map[string]*Cmd
+}
+
+// AddCmd adds cmd as a subcommand.
+func (c *Cmd) AddCmd(cmd *Cmd) {
+	if c.children == nil {
+		c.children = make(map[string]*Cmd)
+	}
+	c.children[cmd.Name] = cmd
+}
+
+// DeleteCmd deletes cmd from subcommands.
+func (c *Cmd) DeleteCmd(name string) {
+	delete(c.children, name)
+}
+
+// Children returns the subcommands of c.
+func (c *Cmd) Children() []*Cmd {
+	var cmds []*Cmd
+	for _, cmd := range c.children {
+		cmds = append(cmds, cmd)
+	}
+	sort.Sort(cmdSorter(cmds))
+	return cmds
+}
+
+func (c *Cmd) hasSubcommand() bool {
+	if len(c.children) > 1 {
+		return true
+	}
+	if _, ok := c.children["help"]; !ok {
+		return len(c.children) > 0
+	}
+	return false
+}
+
+// HelpText returns the computed help of the command and its subcommands.
+func (c Cmd) HelpText() string {
+	var b bytes.Buffer
+	p := func(s ...interface{}) {
+		fmt.Fprintln(&b)
+		if len(s) > 0 {
+			fmt.Fprintln(&b, s...)
+		}
+	}
+	if c.LongHelp != "" {
+		p(c.LongHelp)
+	} else if c.Help != "" {
+		p(c.Help)
+	} else if c.Name != "" {
+		p(c.Name, "has no help")
+	}
+	if c.hasSubcommand() {
+		p("Commands:")
+		w := tabwriter.NewWriter(&b, 0, 4, 2, ' ', 0)
+		for _, child := range c.Children() {
+			fmt.Fprintf(w, "\t%s\t\t\t%s\n", child.Name, child.Help)
+		}
+		w.Flush()
+		p()
+	}
+	return b.String()
+}
+
+// findChildCmd returns the subcommand with matching name or alias.
+func (c *Cmd) findChildCmd(name string) *Cmd {
+	// find perfect matches first
+	if cmd, ok := c.children[name]; ok {
+		return cmd
+	}
+
+	// find alias matching the name
+	for _, cmd := range c.children {
+		for _, alias := range cmd.Aliases {
+			if alias == name {
+				return cmd
+			}
+		}
+	}
+
+	return nil
+}
+
+// FindCmd finds the matching Cmd for args.
+// It returns the Cmd and the remaining args.
+func (c Cmd) FindCmd(args []string) (*Cmd, []string) {
+	var cmd *Cmd
+	for i, arg := range args {
+		if cmd1 := c.findChildCmd(arg); cmd1 != nil {
+			cmd = cmd1
+			c = *cmd
+			continue
+		}
+		return cmd, args[i:]
+	}
+	return cmd, nil
+}
+
+type cmdSorter []*Cmd
+
+func (c cmdSorter) Len() int           { return len(c) }
+func (c cmdSorter) Less(i, j int) bool { return c[i].Name < c[j].Name }
+func (c cmdSorter) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/command_test.go b/vendor/gopkg.in/abiosoft/ishell.v1/command_test.go
new file mode 100644
index 0000000..27c89a0
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/command_test.go
@@ -0,0 +1,96 @@
+package ishell_test
+
+import (
+	"testing"
+
+	"github.com/abiosoft/ishell"
+	"github.com/stretchr/testify/assert"
+)
+
+func newCmd(name string, help string) *ishell.Cmd {
+	return &ishell.Cmd{
+		Name: name,
+		Help: help,
+	}
+}
+
+func TestAddCommand(t *testing.T) {
+	cmd := newCmd("root", "")
+	assert.Equal(t, len(cmd.Children()), 0, "should be empty")
+	cmd.AddCmd(newCmd("child", ""))
+	assert.Equal(t, len(cmd.Children()), 1, "should include one child command")
+}
+
+func TestDeleteCommand(t *testing.T) {
+	cmd := newCmd("root", "")
+	cmd.AddCmd(newCmd("child", ""))
+	assert.Equal(t, len(cmd.Children()), 1, "should include one child command")
+	cmd.DeleteCmd("child")
+	assert.Equal(t, len(cmd.Children()), 0, "should be empty")
+}
+
+func TestFindCmd(t *testing.T) {
+	cmd := newCmd("root", "")
+	cmd.AddCmd(newCmd("child1", ""))
+	cmd.AddCmd(newCmd("child2", ""))
+	res, err := cmd.FindCmd([]string{"child1"})
+	if err != nil {
+		t.Fatal("finding should work")
+	}
+	assert.Equal(t, res.Name, "child1")
+
+	res, err = cmd.FindCmd([]string{"child2"})
+	if err != nil {
+		t.Fatal("finding should work")
+	}
+	assert.Equal(t, res.Name, "child2")
+
+	res, err = cmd.FindCmd([]string{"child3"})
+	if err == nil {
+		t.Fatal("should not find this child!")
+	}
+	assert.Nil(t, res)
+}
+
+func TestFindAlias(t *testing.T) {
+	cmd := newCmd("root", "")
+	subcmd := newCmd("child1", "")
+	subcmd.Aliases = []string{"alias1", "alias2"}
+	cmd.AddCmd(subcmd)
+
+	res, err := cmd.FindCmd([]string{"alias1"})
+	if err != nil {
+		t.Fatal("finding alias should work")
+	}
+	assert.Equal(t, res.Name, "child1")
+
+	res, err = cmd.FindCmd([]string{"alias2"})
+	if err != nil {
+		t.Fatal("finding alias should work")
+	}
+	assert.Equal(t, res.Name, "child1")
+
+	res, err = cmd.FindCmd([]string{"alias3"})
+	if err == nil {
+		t.Fatal("should not find this child!")
+	}
+	assert.Nil(t, res)
+}
+
+func TestHelpText(t *testing.T) {
+	cmd := newCmd("root", "help for root command")
+	cmd.AddCmd(newCmd("child1", "help for child1 command"))
+	cmd.AddCmd(newCmd("child2", "help for child2 command"))
+	res := cmd.HelpText()
+	expected := "\nhelp for root command\n\nCommands:\n  child1      help for child1 command\n  child2      help for child2 command\n\n"
+	assert.Equal(t, res, expected)
+}
+
+func TestChildrenSortedAlphabetically(t *testing.T) {
+	cmd := newCmd("root", "help for root command")
+	cmd.AddCmd(newCmd("child2", "help for child1 command"))
+	cmd.AddCmd(newCmd("child1", "help for child2 command"))
+	children := cmd.Children()
+	assert.Equal(t, children[0].Name, "child1", "must be first")
+	assert.Equal(t, children[1].Name, "child2", "must be second")
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/completer.go b/vendor/gopkg.in/abiosoft/ishell.v1/completer.go
new file mode 100644
index 0000000..4ffed29
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/completer.go
@@ -0,0 +1,59 @@
+package ishell
+
+import (
+	"strings"
+
+	"github.com/flynn-archive/go-shlex"
+)
+
+type iCompleter struct {
+	cmd      *Cmd
+	disabled func() bool
+}
+
+func (ic iCompleter) Do(line []rune, pos int) (newLine [][]rune, length int) {
+	if ic.disabled != nil && ic.disabled() {
+		return nil, len(line)
+	}
+	var words []string
+	if w, err := shlex.Split(string(line)); err == nil {
+		words = w
+	} else {
+		// fall back
+		words = strings.Fields(string(line))
+	}
+
+	var cWords []string
+	prefix := ""
+	if len(words) > 0 && line[pos-1] != ' ' {
+		prefix = words[len(words)-1]
+		cWords = ic.getWords(words[:len(words)-1])
+	} else {
+		cWords = ic.getWords(words)
+	}
+
+	var suggestions [][]rune
+	for _, w := range cWords {
+		if strings.HasPrefix(w, prefix) {
+			suggestions = append(suggestions, []rune(strings.TrimPrefix(w, prefix)))
+		}
+	}
+	if len(suggestions) == 1 && prefix != "" && string(suggestions[0]) == "" {
+		suggestions = [][]rune{[]rune(" ")}
+	}
+	return suggestions, len(prefix)
+}
+
+func (ic iCompleter) getWords(w []string) (s []string) {
+	cmd, args := ic.cmd.FindCmd(w)
+	if cmd == nil {
+		cmd, args = ic.cmd, w
+	}
+	if cmd.Completer != nil {
+		return cmd.Completer(args)
+	}
+	for k := range cmd.children {
+		s = append(s, k)
+	}
+	return
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/context.go b/vendor/gopkg.in/abiosoft/ishell.v1/context.go
new file mode 100644
index 0000000..57805f2
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/context.go
@@ -0,0 +1,58 @@
+package ishell
+
+// Context is an ishell context. It embeds ishell.Actions.
+type Context struct {
+	values      map[string]interface{}
+	progressBar ProgressBar
+	err         error
+
+	// Args is command arguments.
+	Args []string
+
+	// RawArgs is unprocessed command arguments.
+	RawArgs []string
+
+	// Cmd is the currently executing command. This is empty for NotFound and Interrupt.
+	Cmd Cmd
+
+	Actions
+}
+
+// Err informs ishell that an error occurred in the current
+// function.
+func (c *Context) Err(err error) {
+	c.err = err
+}
+
+// Get returns the value associated with this context for key, or nil
+// if no value is associated with key. Successive calls to Get with
+// the same key returns the same result.
+func (c *Context) Get(key string) interface{} {
+	return c.values[key]
+}
+
+// Set sets the key in this context to value.
+func (c *Context) Set(key string, value interface{}) {
+	if c.values == nil {
+		c.values = make(map[string]interface{})
+	}
+	c.values[key] = value
+}
+
+// Del deletes key and its value in this context.
+func (c *Context) Del(key string) {
+	delete(c.values, key)
+}
+
+// Keys returns all keys in the context.
+func (c *Context) Keys() (keys []string) {
+	for key := range c.values {
+		keys = append(keys, key)
+	}
+	return
+}
+
+// ProgressBar returns the progress bar for the current shell context.
+func (c *Context) ProgressBar() ProgressBar {
+	return c.progressBar
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/example/main.go b/vendor/gopkg.in/abiosoft/ishell.v1/example/main.go
new file mode 100644
index 0000000..4ab4b4f
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/example/main.go
@@ -0,0 +1,216 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/abiosoft/ishell"
+	"github.com/fatih/color"
+)
+
+func main() {
+	shell := ishell.New()
+
+	// display info.
+	shell.Println("Sample Interactive Shell")
+
+	// handle login.
+	shell.AddCmd(&ishell.Cmd{
+		Name: "login",
+		Func: func(c *ishell.Context) {
+			c.ShowPrompt(false)
+			defer c.ShowPrompt(true)
+
+			c.Println("Let's simulate login")
+
+			// prompt for input
+			c.Print("Username: ")
+			username := c.ReadLine()
+			c.Print("Password: ")
+			password := c.ReadPassword()
+
+			// do something with username and password
+			c.Println("Your inputs were", username, "and", password+".")
+
+		},
+		Help: "simulate a login",
+	})
+
+	// handle "greet".
+	shell.AddCmd(&ishell.Cmd{
+		Name:    "greet",
+		Aliases: []string{"hello", "welcome"},
+		Help:    "greet user",
+		Func: func(c *ishell.Context) {
+			name := "Stranger"
+			if len(c.Args) > 0 {
+				name = strings.Join(c.Args, " ")
+			}
+			c.Println("Hello", name)
+		},
+	})
+
+	// read multiple lines with "multi" command
+	shell.AddCmd(&ishell.Cmd{
+		Name: "multi",
+		Help: "input in multiple lines",
+		Func: func(c *ishell.Context) {
+			c.Println("Input multiple lines and end with semicolon ';'.")
+			lines := c.ReadMultiLines(";")
+			c.Println("Done reading. You wrote:")
+			c.Println(lines)
+		},
+	})
+
+	// multiple choice
+	shell.AddCmd(&ishell.Cmd{
+		Name: "choice",
+		Help: "multiple choice prompt",
+		Func: func(c *ishell.Context) {
+			choice := c.MultiChoice([]string{
+				"Golangers",
+				"Go programmers",
+				"Gophers",
+				"Goers",
+			}, "What are Go programmers called ?")
+			if choice == 2 {
+				c.Println("You got it!")
+			} else {
+				c.Println("Sorry, you're wrong.")
+			}
+		},
+	})
+
+	// multiple choice
+	shell.AddCmd(&ishell.Cmd{
+		Name: "checklist",
+		Help: "checklist prompt",
+		Func: func(c *ishell.Context) {
+			languages := []string{"Python", "Go", "Haskell", "Rust"}
+			choices := c.Checklist(languages,
+				"What are your favourite programming languages ?",
+				nil)
+			out := func() (c []string) {
+				for _, v := range choices {
+					c = append(c, languages[v])
+				}
+				return
+			}
+			c.Println("Your choices are", strings.Join(out(), ", "))
+		},
+	})
+
+	// progress bars
+	{
+		// determinate
+		shell.AddCmd(&ishell.Cmd{
+			Name: "det",
+			Help: "determinate progress bar",
+			Func: func(c *ishell.Context) {
+				c.ProgressBar().Start()
+				for i := 0; i < 101; i++ {
+					c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
+					c.ProgressBar().Progress(i)
+					time.Sleep(time.Millisecond * 100)
+				}
+				c.ProgressBar().Stop()
+			},
+		})
+
+		// indeterminate
+		shell.AddCmd(&ishell.Cmd{
+			Name: "ind",
+			Help: "indeterminate progress bar",
+			Func: func(c *ishell.Context) {
+				c.ProgressBar().Indeterminate(true)
+				c.ProgressBar().Start()
+				time.Sleep(time.Second * 10)
+				c.ProgressBar().Stop()
+			},
+		})
+	}
+
+	// subcommands and custom autocomplete.
+	{
+		var words []string
+		autoCmd := &ishell.Cmd{
+			Name: "suggest",
+			Help: "try auto complete",
+			LongHelp: `Try dynamic autocomplete by adding and removing words.
+Then view the autocomplete by tabbing after "words" subcommand.
+
+This is an example of a long help.`,
+		}
+		autoCmd.AddCmd(&ishell.Cmd{
+			Name: "add",
+			Help: "add words to autocomplete",
+			Func: func(c *ishell.Context) {
+				if len(c.Args) == 0 {
+					c.Err(errors.New("missing word(s)"))
+					return
+				}
+				words = append(words, c.Args...)
+			},
+		})
+
+		autoCmd.AddCmd(&ishell.Cmd{
+			Name: "clear",
+			Help: "clear words in autocomplete",
+			Func: func(c *ishell.Context) {
+				words = nil
+			},
+		})
+
+		autoCmd.AddCmd(&ishell.Cmd{
+			Name: "words",
+			Help: "add words with 'suggest add', then tab after typing 'suggest words '",
+			Completer: func([]string) []string {
+				return words
+			},
+		})
+
+		shell.AddCmd(autoCmd)
+	}
+
+	shell.AddCmd(&ishell.Cmd{
+		Name: "paged",
+		Help: "show paged text",
+		Func: func(c *ishell.Context) {
+			lines := ""
+			line := `%d. This is a paged text input.
+This is another line of it.
+
+`
+			for i := 0; i < 100; i++ {
+				lines += fmt.Sprintf(line, i+1)
+			}
+			c.ShowPaged(lines)
+		},
+	})
+
+	cyan := color.New(color.FgCyan).SprintFunc()
+	yellow := color.New(color.FgYellow).SprintFunc()
+	boldRed := color.New(color.FgRed, color.Bold).SprintFunc()
+	shell.AddCmd(&ishell.Cmd{
+		Name: "color",
+		Help: "color print",
+		Func: func(c *ishell.Context) {
+			c.Print(cyan("cyan\n"))
+			c.Println(yellow("yellow"))
+			c.Printf("%s\n", boldRed("bold red"))
+		},
+	})
+
+	// when started with "exit" as first argument, assume non-interactive execution
+	if len(os.Args) > 1 && os.Args[1] == "exit" {
+		shell.Process(os.Args[2:]...)
+	} else {
+		// start shell
+		shell.Run()
+		// teardown
+		shell.Close()
+	}
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/functions.go b/vendor/gopkg.in/abiosoft/ishell.v1/functions.go
new file mode 100644
index 0000000..886a5a1
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/functions.go
@@ -0,0 +1,47 @@
+package ishell
+
+import (
+	"os"
+)
+
+func exitFunc(c *Context) {
+	c.Stop()
+}
+
+func helpFunc(c *Context) {
+	c.Println(c.HelpText())
+}
+
+func clearFunc(c *Context) {
+	err := c.ClearScreen()
+	if err != nil {
+		c.Err(err)
+	}
+}
+
+func addDefaultFuncs(s *Shell) {
+	s.AddCmd(&Cmd{
+		Name: "exit",
+		Help: "exit the program",
+		Func: exitFunc,
+	})
+	s.AddCmd(&Cmd{
+		Name: "help",
+		Help: "display help",
+		Func: helpFunc,
+	})
+	s.AddCmd(&Cmd{
+		Name: "clear",
+		Help: "clear the screen",
+		Func: clearFunc,
+	})
+	s.Interrupt(interruptFunc)
+}
+
+func interruptFunc(c *Context, count int, line string) {
+	if count >= 2 {
+		c.Println("Interrupted")
+		os.Exit(1)
+	}
+	c.Println("Input Ctrl-c once more to exit")
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/ishell.go b/vendor/gopkg.in/abiosoft/ishell.v1/ishell.go
new file mode 100644
index 0000000..ccefb45
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/ishell.go
@@ -0,0 +1,675 @@
+// Package ishell implements an interactive shell.
+package ishell
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+	"unicode"
+
+	"github.com/chzyer/readline"
+	"github.com/fatih/color"
+	"github.com/flynn-archive/go-shlex"
+)
+
+const (
+	defaultPrompt      = ">>> "
+	defaultMultiPrompt = "... "
+)
+
+var (
+	errNoHandler          = errors.New("incorrect input, try 'help'")
+	errNoInterruptHandler = errors.New("no interrupt handler")
+)
+
+// Shell is an interactive cli shell.
+type Shell struct {
+	rootCmd           *Cmd
+	generic           func(*Context)
+	interrupt         func(*Context, int, string)
+	interruptCount    int
+	eof               func(*Context)
+	reader            *shellReader
+	writer            io.Writer
+	active            bool
+	activeMutex       sync.RWMutex
+	ignoreCase        bool
+	customCompleter   bool
+	multiChoiceActive bool
+	haltChan          chan struct{}
+	historyFile       string
+	contextValues     map[string]interface{}
+	autoHelp          bool
+	rawArgs           []string
+	progressBar       ProgressBar
+	Actions
+}
+
+// New creates a new shell with default settings. Uses standard output and default prompt ">> ".
+func New() *Shell {
+	return NewWithConfig(&readline.Config{Prompt: defaultPrompt})
+}
+
+// NewWithConfig creates a new shell with custom readline config.
+func NewWithConfig(conf *readline.Config) *Shell {
+	rl, err := readline.NewEx(conf)
+	if err != nil {
+		log.Println("Shell or operating system not supported.")
+		log.Fatal(err)
+	}
+	shell := &Shell{
+		rootCmd: &Cmd{},
+		reader: &shellReader{
+			scanner:     rl,
+			prompt:      rl.Config.Prompt,
+			multiPrompt: defaultMultiPrompt,
+			showPrompt:  true,
+			buf:         &bytes.Buffer{},
+			completer:   readline.NewPrefixCompleter(),
+		},
+		writer:   conf.Stdout,
+		autoHelp: true,
+	}
+	shell.Actions = &shellActionsImpl{Shell: shell}
+	shell.progressBar = newProgressBar(shell)
+	addDefaultFuncs(shell)
+	return shell
+}
+
+// Start starts the shell but does not wait for it to stop.
+func (s *Shell) Start() {
+	s.prepareRun()
+	go s.run()
+}
+
+// Run starts the shell and waits for it to stop.
+func (s *Shell) Run() {
+	s.prepareRun()
+	s.run()
+}
+
+// Wait waits for the shell to stop.
+func (s *Shell) Wait() {
+	<-s.haltChan
+}
+
+func (s *Shell) stop() {
+	if !s.Active() {
+		return
+	}
+	s.activeMutex.Lock()
+	s.active = false
+	s.activeMutex.Unlock()
+	close(s.haltChan)
+}
+
+// Close stops the shell (if required) and closes the shell's input.
+// This should be called when done with reading inputs.
+// Unlike `Stop`, a closed shell cannot be restarted.
+func (s *Shell) Close() {
+	s.stop()
+	s.reader.scanner.Close()
+}
+
+func (s *Shell) prepareRun() {
+	if s.Active() {
+		return
+	}
+	if !s.customCompleter {
+		s.initCompleters()
+	}
+	s.activeMutex.Lock()
+	s.active = true
+	s.activeMutex.Unlock()
+
+	s.haltChan = make(chan struct{})
+}
+
+func (s *Shell) run() {
+shell:
+	for s.Active() {
+		var line []string
+		var err error
+		read := make(chan struct{})
+		go func() {
+			line, err = s.read()
+			read <- struct{}{}
+		}()
+		select {
+		case <-read:
+			break
+		case <-s.haltChan:
+			continue shell
+		}
+
+		if err == io.EOF {
+			if s.eof == nil {
+				fmt.Println("EOF")
+				break
+			}
+			if err := handleEOF(s); err != nil {
+				s.Println("Error:", err)
+				continue
+			}
+		} else if err != nil && err != readline.ErrInterrupt {
+			s.Println("Error:", err)
+			continue
+		}
+
+		if err == readline.ErrInterrupt {
+			// interrupt received
+			err = handleInterrupt(s, line)
+		} else {
+			// reset interrupt counter
+			s.interruptCount = 0
+
+			// normal flow
+			if len(line) == 0 {
+				// no input line
+				continue
+			}
+
+			err = handleInput(s, line)
+		}
+		if err != nil {
+			s.Println("Error:", err)
+		}
+	}
+}
+
+// Active tells if the shell is active. i.e. Start is previously called.
+func (s *Shell) Active() bool {
+	s.activeMutex.RLock()
+	defer s.activeMutex.RUnlock()
+	return s.active
+}
+
+// Process runs shell using args in a non-interactive mode.
+func (s *Shell) Process(args ...string) error {
+	return handleInput(s, args)
+}
+
+func handleInput(s *Shell, line []string) error {
+	handled, err := s.handleCommand(line)
+	if handled || err != nil {
+		return err
+	}
+
+	// Generic handler
+	if s.generic == nil {
+		return errNoHandler
+	}
+	c := newContext(s, nil, line)
+	s.generic(c)
+	return c.err
+}
+
+func handleInterrupt(s *Shell, line []string) error {
+	if s.interrupt == nil {
+		return errNoInterruptHandler
+	}
+	c := newContext(s, nil, line)
+	s.interruptCount++
+	s.interrupt(c, s.interruptCount, strings.Join(line, " "))
+	return c.err
+}
+
+func handleEOF(s *Shell) error {
+	c := newContext(s, nil, nil)
+	s.eof(c)
+	return c.err
+}
+
+func (s *Shell) handleCommand(str []string) (bool, error) {
+	if s.ignoreCase {
+		for i := range str {
+			str[i] = strings.ToLower(str[i])
+		}
+	}
+	cmd, args := s.rootCmd.FindCmd(str)
+	if cmd == nil {
+		return false, nil
+	}
+	// trigger help if func is not registered or auto help is true
+	if cmd.Func == nil || (s.autoHelp && len(args) == 1 && args[0] == "help") {
+		s.Println(cmd.HelpText())
+		return true, nil
+	}
+	c := newContext(s, cmd, args)
+	cmd.Func(c)
+	return true, c.err
+}
+
+func (s *Shell) readLine() (line string, err error) {
+	consumer := make(chan lineString)
+	defer close(consumer)
+	go s.reader.readLine(consumer)
+	ls := <-consumer
+	return ls.line, ls.err
+}
+
+func (s *Shell) read() ([]string, error) {
+	s.rawArgs = nil
+	heredoc := false
+	eof := ""
+	// heredoc multiline
+	lines, err := s.readMultiLinesFunc(func(line string) bool {
+		if !heredoc {
+			if strings.Contains(line, "<<") {
+				s := strings.SplitN(line, "<<", 2)
+				if eof = strings.TrimSpace(s[1]); eof != "" {
+					heredoc = true
+					return true
+				}
+			}
+		} else {
+			return line != eof
+		}
+		return strings.HasSuffix(strings.TrimSpace(line), "\\")
+	})
+
+	s.rawArgs = strings.Fields(lines)
+
+	if heredoc {
+		s := strings.SplitN(lines, "<<", 2)
+		args, err1 := shlex.Split(s[0])
+
+		arg := strings.TrimSuffix(strings.SplitN(s[1], "\n", 2)[1], eof)
+		args = append(args, arg)
+		if err1 != nil {
+			return args, err1
+		}
+		return args, err
+	}
+
+	lines = strings.Replace(lines, "\\\n", " \n", -1)
+
+	args, err1 := shlex.Split(lines)
+	if err1 != nil {
+		return args, err1
+	}
+
+	return args, err
+}
+
+func (s *Shell) readMultiLinesFunc(f func(string) bool) (string, error) {
+	var lines bytes.Buffer
+	currentLine := 0
+	var err error
+	for {
+		if currentLine == 1 {
+			// from second line, enable next line prompt.
+			s.reader.setMultiMode(true)
+		}
+		var line string
+		line, err = s.readLine()
+		fmt.Fprint(&lines, line)
+		if !f(line) || err != nil {
+			break
+		}
+		fmt.Fprintln(&lines)
+		currentLine++
+	}
+	if currentLine > 0 {
+		// if more than one line is read
+		// revert to standard prompt.
+		s.reader.setMultiMode(false)
+	}
+	return lines.String(), err
+}
+
+func (s *Shell) initCompleters() {
+	s.setCompleter(iCompleter{cmd: s.rootCmd, disabled: func() bool { return s.multiChoiceActive }})
+}
+
+func (s *Shell) setCompleter(completer readline.AutoCompleter) {
+	config := s.reader.scanner.Config.Clone()
+	config.AutoComplete = completer
+	s.reader.scanner.SetConfig(config)
+}
+
+// CustomCompleter allows use of custom implementation of readline.Autocompleter.
+func (s *Shell) CustomCompleter(completer readline.AutoCompleter) {
+	s.customCompleter = true
+	s.setCompleter(completer)
+}
+
+// AddCmd adds a new command handler.
+// This only adds top level commands.
+func (s *Shell) AddCmd(cmd *Cmd) {
+	s.rootCmd.AddCmd(cmd)
+}
+
+// DeleteCmd deletes a top level command.
+func (s *Shell) DeleteCmd(name string) {
+	s.rootCmd.DeleteCmd(name)
+}
+
+// NotFound adds a generic function for all inputs.
+// It is called if the shell input could not be handled by any of the
+// added commands.
+func (s *Shell) NotFound(f func(*Context)) {
+	s.generic = f
+}
+
+// AutoHelp sets if ishell should trigger help message if
+// a command's arg is "help". Defaults to true.
+//
+// This can be set to false for more control on how help is
+// displayed.
+func (s *Shell) AutoHelp(enable bool) {
+	s.autoHelp = enable
+}
+
+// Interrupt adds a function to handle keyboard interrupt (Ctrl-c).
+// count is the number of consecutive times that Ctrl-c has been pressed.
+// i.e. any input apart from Ctrl-c resets count to 0.
+func (s *Shell) Interrupt(f func(c *Context, count int, input string)) {
+	s.interrupt = f
+}
+
+// EOF adds a function to handle End of File input (Ctrl-d).
+// This overrides the default behaviour which terminates the shell.
+func (s *Shell) EOF(f func(c *Context)) {
+	s.eof = f
+}
+
+// SetHistoryPath sets where readlines history file location. Use an empty
+// string to disable history file. It is empty by default.
+func (s *Shell) SetHistoryPath(path string) {
+	// Using scanner.SetHistoryPath doesn't initialize things properly and
+	// history file is never written. Simpler to just create a new readline
+	// Instance.
+	config := s.reader.scanner.Config.Clone()
+	config.HistoryFile = path
+	s.reader.scanner, _ = readline.NewEx(config)
+}
+
+// SetHomeHistoryPath is a convenience method that sets the history path
+// in user's home directory.
+func (s *Shell) SetHomeHistoryPath(path string) {
+	home := os.Getenv("HOME")
+	if runtime.GOOS == "windows" {
+		home = os.Getenv("USERPROFILE")
+	}
+	abspath := filepath.Join(home, path)
+	s.SetHistoryPath(abspath)
+}
+
+// SetOut sets the writer to write outputs to.
+func (s *Shell) SetOut(writer io.Writer) {
+	s.writer = writer
+}
+
+func initSelected(init []int, max int) []int {
+	selectedMap := make(map[int]bool)
+	for _, i := range init {
+		if i < max {
+			selectedMap[i] = true
+		}
+	}
+	selected := make([]int, len(selectedMap))
+	i := 0
+	for k := range selectedMap {
+		selected[i] = k
+		i++
+	}
+	return selected
+}
+
+func toggle(selected []int, cur int) []int {
+	for i, s := range selected {
+		if s == cur {
+			return append(selected[:i], selected[i+1:]...)
+		}
+	}
+	return append(selected, cur)
+}
+
+func (s *Shell) multiChoice(options []string, text string, init []int, multiResults bool) []int {
+	s.multiChoiceActive = true
+	defer func() { s.multiChoiceActive = false }()
+
+	conf := s.reader.scanner.Config.Clone()
+
+	conf.DisableAutoSaveHistory = true
+
+	conf.FuncFilterInputRune = func(r rune) (rune, bool) {
+		switch r {
+		case 16:
+			return -1, true
+		case 14:
+			return -2, true
+		case 32:
+			return -3, true
+
+		}
+		return r, true
+	}
+
+	var selected []int
+	if multiResults {
+		selected = initSelected(init, len(options))
+	}
+
+	s.ShowPrompt(false)
+	defer s.ShowPrompt(true)
+
+	// TODO this may not work on windows.
+	s.Print("\033[?25l")
+	defer s.Print("\033[?25h")
+
+	cur := 0
+	if len(selected) > 0 {
+		cur = selected[len(selected)-1]
+	}
+
+	_, curRow, err := getPosition()
+	if err != nil {
+		return nil
+	}
+
+	_, maxRows, err := readline.GetSize(0)
+	if err != nil {
+		return nil
+	}
+
+	// allocate some space to be at the top of the screen
+	s.Printf("\033[%dS", curRow)
+
+	// move cursor to the top
+	// TODO it happens on every update, however, some trash appears in history without this line
+	s.Print("\033[0;0H")
+
+	offset := 0
+
+	update := func() {
+		strs := buildOptionsStrings(options, selected, cur)
+		if len(strs) > maxRows-1 {
+			strs = strs[offset : maxRows+offset-1]
+		}
+		s.Print("\033[0;0H")
+		// clear from the cursor to the end of the screen
+		s.Print("\033[0J")
+		s.Println(text)
+		s.Print(strings.Join(strs, "\n"))
+	}
+	var lastKey rune
+	refresh := make(chan struct{}, 1)
+	listener := func(line []rune, pos int, key rune) (newline []rune, newPos int, ok bool) {
+		lastKey = key
+		if key == -2 {
+			cur++
+			if cur >= maxRows+offset-1 {
+				offset++
+			}
+			if cur >= len(options) {
+				offset = 0
+				cur = 0
+			}
+		} else if key == -1 {
+			cur--
+			if cur < offset {
+				offset--
+			}
+			if cur < 0 {
+				if len(options) > maxRows-1 {
+					offset = len(options) - maxRows + 1
+				} else {
+					offset = 0
+				}
+				cur = len(options) - 1
+			}
+		} else if key == -3 {
+			if multiResults {
+				selected = toggle(selected, cur)
+			}
+		}
+		refresh <- struct{}{}
+		return
+	}
+	conf.Listener = readline.FuncListener(listener)
+	oldconf := s.reader.scanner.SetConfig(conf)
+
+	stop := make(chan struct{})
+	defer func() {
+		stop <- struct{}{}
+		s.Println()
+	}()
+	t := time.NewTicker(time.Millisecond * 200)
+	defer t.Stop()
+	go func() {
+		for {
+			select {
+			case <-stop:
+				return
+			case <-refresh:
+				update()
+			case <-t.C:
+				_, rows, _ := readline.GetSize(0)
+				if maxRows != rows {
+					maxRows = rows
+					update()
+				}
+			}
+		}
+	}()
+	s.ReadLine()
+
+	s.reader.scanner.SetConfig(oldconf)
+
+	// only handles Ctrl-c for now
+	// this can be broaden later
+	switch lastKey {
+	// Ctrl-c
+	case 3:
+		return []int{-1}
+	}
+	if multiResults {
+		return selected
+	}
+	return []int{cur}
+}
+
+func buildOptionsStrings(options []string, selected []int, index int) []string {
+	var strs []string
+	symbol := " ?"
+	if runtime.GOOS == "windows" {
+		symbol = " >"
+	}
+	for i, opt := range options {
+		mark := "? "
+		if selected == nil {
+			mark = " "
+		}
+		for _, s := range selected {
+			if s == i {
+				mark = "? "
+			}
+		}
+		if i == index {
+			cyan := color.New(color.FgCyan).Add(color.Bold).SprintFunc()
+			strs = append(strs, cyan(symbol+mark+opt))
+		} else {
+			strs = append(strs, "  "+mark+opt)
+		}
+	}
+	return strs
+}
+
+// IgnoreCase specifies whether commands should not be case sensitive.
+// Defaults to false i.e. commands are case sensitive.
+// If true, commands must be registered in lower cases.
+func (s *Shell) IgnoreCase(ignore bool) {
+	s.ignoreCase = ignore
+}
+
+// ProgressBar returns the progress bar for the shell.
+func (s *Shell) ProgressBar() ProgressBar {
+	return s.progressBar
+}
+
+func newContext(s *Shell, cmd *Cmd, args []string) *Context {
+	if cmd == nil {
+		cmd = &Cmd{}
+	}
+	return &Context{
+		Actions:     s.Actions,
+		values:      s.contextValues,
+		progressBar: copyShellProgressBar(s),
+		Args:        args,
+		RawArgs:     s.rawArgs,
+		Cmd:         *cmd,
+	}
+}
+
+func copyShellProgressBar(s *Shell) ProgressBar {
+	sp := s.progressBar.(*progressBarImpl)
+	p := newProgressBar(s)
+	p.Indeterminate(sp.indeterminate)
+	p.Display(sp.display)
+	p.Prefix(sp.prefix)
+	p.Suffix(sp.suffix)
+	p.Final(sp.final)
+	p.Interval(sp.interval)
+	return p
+}
+
+func getPosition() (int, int, error) {
+	state, err := readline.MakeRaw(0)
+	if err != nil {
+		return 0, 0, err
+	}
+	defer readline.Restore(0, state)
+	fmt.Printf("\033[6n")
+	var out string
+	reader := bufio.NewReader(os.Stdin)
+	if err != nil {
+		return 0, 0, err
+	}
+	for {
+		b, err := reader.ReadByte()
+		if err != nil || b == 'R' {
+			break
+		}
+		if unicode.IsPrint(rune(b)) {
+			out += string(b)
+		}
+	}
+	var row, col int
+	_, err = fmt.Sscanf(out, "[%d;%d", &row, &col)
+	if err != nil {
+		return 0, 0, err
+	}
+
+	return col, row, nil
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/progress.go b/vendor/gopkg.in/abiosoft/ishell.v1/progress.go
new file mode 100644
index 0000000..2d73c66
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/progress.go
@@ -0,0 +1,295 @@
+package ishell
+
+import (
+	"fmt"
+	"io"
+	"sync"
+	"time"
+	"unicode/utf8"
+)
+
+// ProgressDisplay handles the display string for
+// a progress bar.
+type ProgressDisplay interface {
+	// Determinate returns the strings to display
+	// for percents 0 to 100.
+	Determinate() [101]string
+	// Indeterminate returns the strings to display
+	// at interval.
+	Indeterminate() []string
+}
+
+// ProgressBar is an ishell progress bar.
+type ProgressBar interface {
+	// Display sets the display of the progress bar.
+	Display(ProgressDisplay)
+	// Indeterminate sets the progress bar type
+	// to indeterminate if true or determinate otherwise.
+	Indeterminate(bool)
+	// Interval sets the time between transitions for indeterminate
+	// progress bar.
+	Interval(time.Duration)
+	// SetProgress sets the progress stage of the progress bar.
+	// percent is from between 1 and 100.
+	Progress(percent int)
+	// Prefix sets the prefix for the output. The text to place before
+	// the display.
+	Prefix(string)
+	// Suffix sets the suffix for the output. The text to place after
+	// the display.
+	Suffix(string)
+	// Final sets the string to show after the progress bar is done.
+	Final(string)
+	// Start starts the progress bar.
+	Start()
+	// Stop stops the progress bar.
+	Stop()
+}
+
+const progressInterval = time.Millisecond * 100
+
+type progressBarImpl struct {
+	display       ProgressDisplay
+	indeterminate bool
+	interval      time.Duration
+	iterator      iterator
+	percent       int
+	prefix        string
+	suffix        string
+	final         string
+	writer        io.Writer
+	writtenLen    int
+	running       bool
+	wait          chan struct{}
+	wMutex        sync.Mutex
+	sync.Mutex
+}
+
+func newProgressBar(s *Shell) ProgressBar {
+	display := simpleProgressDisplay{}
+	return &progressBarImpl{
+		interval:      progressInterval,
+		writer:        s.writer,
+		display:       display,
+		iterator:      &stringIterator{set: display.Indeterminate()},
+		indeterminate: true,
+	}
+}
+
+func (p *progressBarImpl) Display(display ProgressDisplay) {
+	p.display = display
+}
+
+func (p *progressBarImpl) Indeterminate(b bool) {
+	p.indeterminate = b
+}
+
+func (p *progressBarImpl) Interval(t time.Duration) {
+	p.interval = t
+}
+
+func (p *progressBarImpl) Progress(percent int) {
+	if percent < 0 {
+		percent = 0
+	} else if percent > 100 {
+		percent = 100
+	}
+	p.percent = percent
+	p.indeterminate = false
+	p.refresh()
+}
+
+func (p *progressBarImpl) Prefix(prefix string) {
+	p.prefix = prefix
+}
+
+func (p *progressBarImpl) Suffix(suffix string) {
+	p.suffix = suffix
+}
+
+func (p *progressBarImpl) Final(s string) {
+	p.final = s
+}
+
+func (p *progressBarImpl) write(s string) error {
+	p.erase(p.writtenLen)
+	p.writtenLen = utf8.RuneCountInString(s)
+	_, err := p.writer.Write([]byte(s))
+	return err
+}
+
+func (p *progressBarImpl) erase(n int) {
+	for i := 0; i < n; i++ {
+		p.writer.Write([]byte{'\b'})
+	}
+}
+
+func (p *progressBarImpl) done() {
+	p.wMutex.Lock()
+	defer p.wMutex.Unlock()
+
+	p.erase(p.writtenLen)
+	fmt.Fprintln(p.writer, p.final)
+}
+
+func (p *progressBarImpl) output() string {
+	p.Lock()
+	defer p.Unlock()
+
+	var display string
+	if p.indeterminate {
+		display = p.iterator.next()
+	} else {
+		display = p.display.Determinate()[p.percent]
+	}
+	return fmt.Sprintf("%s%s%s ", p.prefix, display, p.suffix)
+}
+
+func (p *progressBarImpl) refresh() {
+	p.wMutex.Lock()
+	defer p.wMutex.Unlock()
+
+	p.write(p.output())
+}
+
+func (p *progressBarImpl) Start() {
+	p.Lock()
+	p.running = true
+	p.wait = make(chan struct{})
+	p.Unlock()
+
+	go func() {
+		for {
+			var running, indeterminate bool
+			p.Lock()
+			running = p.running
+			indeterminate = p.indeterminate
+			p.Unlock()
+
+			if !running {
+				break
+			}
+			time.Sleep(p.interval)
+			if indeterminate {
+				p.refresh()
+			}
+		}
+		p.done()
+		close(p.wait)
+	}()
+}
+
+func (p *progressBarImpl) Stop() {
+	p.Lock()
+	p.running = false
+	p.Unlock()
+
+	<-p.wait
+}
+
+// ProgressDisplayCharSet is the character set for
+// a progress bar.
+type ProgressDisplayCharSet []string
+
+// Determinate satisfies ProgressDisplay interface.
+func (p ProgressDisplayCharSet) Determinate() [101]string {
+	// TODO everything here works but not pleasing to the eyes
+	// and probably not optimal.
+	// This should be cleaner.
+	var set [101]string
+	for i := range set {
+		set[i] = p[len(p)-1]
+	}
+	// assumption is than len(p) <= 101
+	step := 101 / len(p)
+	for i, j := 0, 0; i < len(set) && j < len(p); i, j = i+step, j+1 {
+		for k := 0; k < step && i+k < len(set); k++ {
+			set[i+k] = p[j]
+		}
+	}
+	return set
+}
+
+// Indeterminate satisfies ProgressDisplay interface.
+func (p ProgressDisplayCharSet) Indeterminate() []string {
+	return p
+}
+
+// ProgressDisplayFunc is a convenience function to create a ProgressDisplay.
+// percent is -1 for indeterminate and 0-100 for determinate.
+type ProgressDisplayFunc func(percent int) string
+
+// Determinate satisfies ProgressDisplay interface.
+func (p ProgressDisplayFunc) Determinate() [101]string {
+	var set [101]string
+	for i := range set {
+		set[i] = p(i)
+	}
+	return set
+}
+
+// Indeterminate satisfies ProgressDisplay interface.
+func (p ProgressDisplayFunc) Indeterminate() []string {
+	// loop through until we get back to the first string
+	set := []string{p(-1)}
+	for {
+		next := p(-1)
+		if next == set[0] {
+			break
+		}
+		set = append(set, next)
+	}
+	return set
+}
+
+type iterator interface {
+	next() string
+}
+
+type stringIterator struct {
+	index int
+	set   []string
+}
+
+func (s *stringIterator) next() string {
+	current := s.set[s.index]
+	s.index++
+	if s.index >= len(s.set) {
+		s.index = 0
+	}
+	return current
+}
+
+var (
+	indeterminateCharSet = []string{
+		"[====                ]", "[ ====               ]", "[  ====              ]",
+		"[   ====             ]", "[    ====            ]", "[     ====           ]",
+		"[      ====          ]", "[       ====         ]", "[        ====        ]",
+		"[         ====       ]", "[          ====      ]", "[           ====     ]",
+		"[            ====    ]", "[             ====   ]", "[              ====  ]",
+		"[               ==== ]", "[                ====]",
+		"[               ==== ]", "[              ====  ]", "[             ====   ]",
+		"[            ====    ]", "[           ====     ]", "[          ====      ]",
+		"[         ====       ]", "[        ====        ]", "[       ====         ]",
+		"[      ====          ]", "[     ====           ]", "[    ====            ]",
+		"[   ====             ]", "[  ====              ]", "[ ====               ]",
+	}
+	determinateCharSet = []string{
+		"[                    ]", "[>                   ]", "[=>                  ]",
+		"[==>                 ]", "[===>                ]", "[====>               ]",
+		"[=====>              ]", "[======>             ]", "[=======>            ]",
+		"[========>           ]", "[=========>          ]", "[==========>         ]",
+		"[===========>        ]", "[============>       ]", "[=============>      ]",
+		"[==============>     ]", "[===============>    ]", "[================>   ]",
+		"[=================>  ]", "[==================> ]", "[===================>]",
+	}
+)
+
+type simpleProgressDisplay struct{}
+
+func (s simpleProgressDisplay) Determinate() [101]string {
+	return ProgressDisplayCharSet(determinateCharSet).Determinate()
+}
+func (s simpleProgressDisplay) Indeterminate() []string {
+	return indeterminateCharSet
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/reader.go b/vendor/gopkg.in/abiosoft/ishell.v1/reader.go
new file mode 100644
index 0000000..447719c
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/reader.go
@@ -0,0 +1,98 @@
+package ishell
+
+import (
+	"bytes"
+	"strings"
+	"sync"
+
+	"github.com/chzyer/readline"
+)
+
+type (
+	lineString struct {
+		line string
+		err  error
+	}
+
+	shellReader struct {
+		scanner      *readline.Instance
+		consumers    chan lineString
+		reading      bool
+		readingMulti bool
+		buf          *bytes.Buffer
+		prompt       string
+		multiPrompt  string
+		showPrompt   bool
+		completer    readline.AutoCompleter
+		sync.Mutex
+	}
+)
+
+// rlPrompt returns the proper prompt for readline based on showPrompt and
+// prompt members.
+func (s *shellReader) rlPrompt() string {
+	if s.showPrompt {
+		if s.readingMulti {
+			return s.multiPrompt
+		}
+		return s.prompt
+	}
+	return ""
+}
+
+func (s *shellReader) readPasswordErr() (string, error) {
+	prompt := ""
+	if s.buf.Len() > 0 {
+		prompt = s.buf.String()
+		s.buf.Truncate(0)
+	}
+	password, err := s.scanner.ReadPassword(prompt)
+	return string(password), err
+}
+
+func (s *shellReader) readPassword() string {
+	password, _ := s.readPasswordErr()
+	return password
+}
+
+func (s *shellReader) setMultiMode(use bool) {
+	s.readingMulti = use
+}
+
+func (s *shellReader) readLine(consumer chan lineString) {
+	s.Lock()
+	defer s.Unlock()
+
+	// already reading
+	if s.reading {
+		return
+	}
+	s.reading = true
+	// start reading
+
+	// detect if print is called to
+	// prevent readline lib from clearing line.
+	// use the last line as prompt.
+	// TODO find better way.
+	shellPrompt := s.prompt
+	prompt := s.rlPrompt()
+	if s.buf.Len() > 0 {
+		lines := strings.Split(s.buf.String(), "\n")
+		if p := lines[len(lines)-1]; strings.TrimSpace(p) != "" {
+			prompt = p
+		}
+		s.buf.Truncate(0)
+	}
+
+	// use printed statement as prompt
+	s.scanner.SetPrompt(prompt)
+
+	line, err := s.scanner.Readline()
+
+	// reset prompt
+	s.scanner.SetPrompt(shellPrompt)
+
+	ls := lineString{string(line), err}
+	consumer <- ls
+	s.reading = false
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/utils_unix.go b/vendor/gopkg.in/abiosoft/ishell.v1/utils_unix.go
new file mode 100644
index 0000000..2a9c745
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/utils_unix.go
@@ -0,0 +1,12 @@
+// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris
+
+package ishell
+
+import (
+	"github.com/chzyer/readline"
+)
+
+func clearScreen(s *Shell) error {
+	_, err := readline.ClearScreen(s.writer)
+	return err
+}
diff --git a/vendor/gopkg.in/abiosoft/ishell.v1/utils_windows.go b/vendor/gopkg.in/abiosoft/ishell.v1/utils_windows.go
new file mode 100644
index 0000000..3351cd1
--- /dev/null
+++ b/vendor/gopkg.in/abiosoft/ishell.v1/utils_windows.go
@@ -0,0 +1,11 @@
+// +build windows
+
+package ishell
+
+import (
+	"github.com/chzyer/readline"
+)
+
+func clearScreen(s *Shell) error {
+	return readline.ClearScreen(s.writer)
+}
diff --git a/vendor/gopkg.in/readline.v1/.gitignore b/vendor/gopkg.in/readline.v1/.gitignore
new file mode 100644
index 0000000..d128cc4
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/.gitignore
@@ -0,0 +1,2 @@
+*.exe
+*.tmp
diff --git a/vendor/gopkg.in/readline.v1/.travis.yml b/vendor/gopkg.in/readline.v1/.travis.yml
new file mode 100644
index 0000000..b35dfc1
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/.travis.yml
@@ -0,0 +1,10 @@
+language: go
+go:
+  - 1.5
+before_install:
+  - go get golang.org/x/crypto/ssh/terminal
+script:
+  - GOOS=windows go install github.com/chzyer/readline/example/...
+  - GOOS=linux go install github.com/chzyer/readline/example/...
+  - GOOS=darwin go install github.com/chzyer/readline/example/...
+  - go test -v
diff --git a/vendor/gopkg.in/readline.v1/CHANGELOG.md b/vendor/gopkg.in/readline.v1/CHANGELOG.md
new file mode 100644
index 0000000..5c1811a
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/CHANGELOG.md
@@ -0,0 +1,58 @@
+# ChangeLog
+
+### 1.4 - 2016-07-25
+
+* [#60][60] Support dynamic autocompletion
+* Fix ANSI parser on Windows
+* Fix wrong column width in complete mode on Windows
+* Remove dependent package "golang.org/x/crypto/ssh/terminal"
+
+### 1.3 - 2016-05-09
+
+* [#38][38] add SetChildren for prefix completer interface
+* [#42][42] improve multiple lines compatibility
+* [#43][43] remove sub-package(runes) for gopkg compatiblity
+* [#46][46] Auto complete with space prefixed line
+* [#48][48]	support suspend process (ctrl+Z)
+* [#49][49] fix bug that check equals with previous command
+* [#53][53] Fix bug which causes integer divide by zero panicking when input buffer is empty
+
+### 1.2 - 2016-03-05
+
+* Add a demo for checking password strength [example/readline-pass-strength](https://github.com/chzyer/readline/blob/master/example/readline-pass-strength/readline-pass-strength.go), , written by [@sahib](https://github.com/sahib)
+* [#23][23], support stdin remapping
+* [#27][27], add a `UniqueEditLine` to `Config`, which will erase the editing line after user submited it, usually use in IM.
+* Add a demo for multiline [example/readline-multiline](https://github.com/chzyer/readline/blob/master/example/readline-multiline/readline-multiline.go) which can submit one SQL by multiple lines.
+* Supports performs even stdin/stdout is not a tty.
+* Add a new simple apis for single instance, check by [here](https://github.com/chzyer/readline/blob/master/std.go). It need to save history manually if using this api.
+* [#28][28], fixes the history is not working as expected.
+* [#33][33], vim mode now support `c`, `d`, `x (delete character)`, `r (replace character)`
+
+### 1.1 - 2015-11-20
+
+* [#12][12] Add support for key `<Delete>`/`<Home>`/`<End>`
+* Only enter raw mode as needed (calling `Readline()`), program will receive signal(e.g. Ctrl+C) if not interact with `readline`.
+* Bugs fixed for `PrefixCompleter`
+* Press `Ctrl+D` in empty line will cause `io.EOF` in error, Press `Ctrl+C` in anytime will cause `ErrInterrupt` instead of `io.EOF`, this will privodes a shell-like user experience.
+* Customable Interrupt/EOF prompt in `Config`
+* [#17][17] Change atomic package to use 32bit function to let it runnable on arm 32bit devices
+* Provides a new password user experience(`readline.ReadPasswordEx()`).
+
+### 1.0 - 2015-10-14
+
+* Initial public release.
+
+[12]: https://github.com/chzyer/readline/pull/12
+[17]: https://github.com/chzyer/readline/pull/17
+[23]: https://github.com/chzyer/readline/pull/23
+[27]: https://github.com/chzyer/readline/pull/27
+[28]: https://github.com/chzyer/readline/pull/28
+[33]: https://github.com/chzyer/readline/pull/33
+[38]: https://github.com/chzyer/readline/pull/38
+[42]: https://github.com/chzyer/readline/pull/42
+[43]: https://github.com/chzyer/readline/pull/43
+[46]: https://github.com/chzyer/readline/pull/46
+[48]: https://github.com/chzyer/readline/pull/48
+[49]: https://github.com/chzyer/readline/pull/49
+[53]: https://github.com/chzyer/readline/pull/53
+[60]: https://github.com/chzyer/readline/pull/60
diff --git a/vendor/gopkg.in/readline.v1/LICENSE b/vendor/gopkg.in/readline.v1/LICENSE
new file mode 100644
index 0000000..c9afab3
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Chzyer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/gopkg.in/readline.v1/README.md b/vendor/gopkg.in/readline.v1/README.md
new file mode 100644
index 0000000..a48cc1a
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/README.md
@@ -0,0 +1,306 @@
+# readline
+
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md)
+[![Build Status](https://travis-ci.org/chzyer/readline.svg?branch=master)](https://travis-ci.org/chzyer/readline)
+[![GoDoc](https://godoc.org/github.com/chzyer/readline?status.svg)](https://godoc.org/github.com/chzyer/readline)
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/chzyer/readline?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 
+[![OpenCollective](https://opencollective.com/readline/badge/backers.svg)](#backers)
+[![OpenCollective](https://opencollective.com/readline/badge/sponsors.svg)](#sponsors)
+
+Readline is A Pure Go Implementation of a libreadline-style Library.  
+The goal is to be a powerful alternater for GNU-Readline.
+
+
+**WHY:**
+Readline will support most of features which GNU Readline is supported, and provide a pure go environment and a MIT license.
+
+It can also provides shell-like interactives by using [flagly](https://github.com/chzyer/flagly) (demo: [flagly-shell](https://github.com/chzyer/flagly/blob/master/demo/flagly-shell/flagly-shell.go))
+
+# Demo
+
+![demo](https://raw.githubusercontent.com/chzyer/readline/assets/demo.gif)
+
+Also works fine in windows
+
+![demo windows](https://raw.githubusercontent.com/chzyer/readline/assets/windows.gif)
+
+
+* [example/readline-demo](https://github.com/chzyer/readline/blob/master/example/readline-demo/readline-demo.go) The source code about the demo above
+
+* [example/readline-im](https://github.com/chzyer/readline/blob/master/example/readline-im/readline-im.go) Example for how to write a IM program.
+
+* [example/readline-multiline](https://github.com/chzyer/readline/blob/master/example/readline-multiline/readline-multiline.go) Example for how to parse command which can submit by multiple time.
+
+* [example/readline-pass-strength](https://github.com/chzyer/readline/blob/master/example/readline-pass-strength/readline-pass-strength.go) A example about checking password strength, written by [@sahib](https://github.com/sahib)
+
+# Todo
+* Vi Mode is not completely finish
+* More funny examples
+* Support dumb/eterm-color terminal in emacs
+
+# Features
+* Support emacs/vi mode, almost all basic features that GNU-Readline is supported
+* zsh-style backward/forward history search
+* zsh-style completion
+* Readline auto refresh when others write to Stdout while editing (it needs specify the Stdout/Stderr provided by *readline.Instance to others).
+* Support colourful prompt in all platforms.
+
+# Usage
+
+* Import package
+
+```
+go get gopkg.in/readline.v1
+```
+
+or
+
+```
+go get github.com/chzyer/readline
+```
+
+* Simplest example
+
+```go
+import "gopkg.in/readline.v1"
+
+rl, err := readline.New("> ")
+if err != nil {
+	panic(err)
+}
+defer rl.Close()
+
+for {
+	line, err := rl.Readline()
+	if err != nil { // io.EOF, readline.ErrInterrupt
+		break
+	}
+	println(line)
+}
+```
+
+* Example with durable history
+
+```go
+rl, err := readline.NewEx(&readline.Config{
+	Prompt: "> ",
+	HistoryFile: "/tmp/readline.tmp",
+})
+if err != nil {
+	panic(err)
+}
+defer rl.Close()
+
+for {
+	line, err := rl.Readline()
+	if err != nil { // io.EOF, readline.ErrInterrupt
+		break
+	}
+	println(line)
+}
+```
+
+* Example with auto completion
+
+```go
+import (
+	"gopkg.in/readline.v1"
+)
+
+var completer = readline.NewPrefixCompleter(
+	readline.PcItem("say",
+		readline.PcItem("hello"),
+		readline.PcItem("bye"),
+	),
+	readline.PcItem("help"),
+)
+
+rl, err := readline.NewEx(&readline.Config{
+	Prompt:       "> ",
+	AutoComplete: completer,
+})
+if err != nil {
+	panic(err)
+}
+defer rl.Close()
+
+for {
+	line, err := rl.Readline()
+	if err != nil { // io.EOF, readline.ErrInterrupt
+		break
+	}
+	println(line)
+}
+```
+
+
+# Shortcut
+
+`Meta`+`B` means press `Esc` and `n` separately.  
+Users can change that in terminal simulator(i.e. iTerm2) to `Alt`+`B`  
+Notice: `Meta`+`B` is equals with `Alt`+`B` in windows.
+
+* Shortcut in normal mode
+
+| Shortcut           | Comment                                  |
+|--------------------|------------------------------------------|
+| `Ctrl`+`A`         | Beginning of line                        |
+| `Ctrl`+`B` / `?`   | Backward one character                   |
+| `Meta`+`B`         | Backward one word                        |
+| `Ctrl`+`C`         | Send io.EOF                              |
+| `Ctrl`+`D`         | Delete one character                     |
+| `Meta`+`D`         | Delete one word                          |
+| `Ctrl`+`E`         | End of line                              |
+| `Ctrl`+`F` / `?`   | Forward one character                    |
+| `Meta`+`F`         | Forward one word                         |
+| `Ctrl`+`G`         | Cancel                                   |
+| `Ctrl`+`H`         | Delete previous character                |
+| `Ctrl`+`I` / `Tab` | Command line completion                  |
+| `Ctrl`+`J`         | Line feed                                |
+| `Ctrl`+`K`         | Cut text to the end of line              |
+| `Ctrl`+`L`         | Clear screen                             |
+| `Ctrl`+`M`         | Same as Enter key                        |
+| `Ctrl`+`N` / `?`   | Next line (in history)                   |
+| `Ctrl`+`P` / `?`   | Prev line (in history)                   |
+| `Ctrl`+`R`         | Search backwards in history              |
+| `Ctrl`+`S`         | Search forwards in history               |
+| `Ctrl`+`T`         | Transpose characters                     |
+| `Meta`+`T`         | Transpose words (TODO)                   |
+| `Ctrl`+`U`         | Cut text to the beginning of line        |
+| `Ctrl`+`W`         | Cut previous word                        |
+| `Backspace`        | Delete previous character                |
+| `Meta`+`Backspace` | Cut previous word                        |
+| `Enter`            | Line feed                                |
+
+
+* Shortcut in Search Mode (`Ctrl`+`S` or `Ctrl`+`r` to enter this mode)
+
+| Shortcut                | Comment                                     |
+|-------------------------|---------------------------------------------|
+| `Ctrl`+`S`              | Search forwards in history                  |
+| `Ctrl`+`R`              | Search backwards in history                 |
+| `Ctrl`+`C` / `Ctrl`+`G` | Exit Search Mode and revert the history     |
+| `Backspace`             | Delete previous character                   |
+| Other                   | Exit Search Mode                            |
+
+* Shortcut in Complete Select Mode (double `Tab` to enter this mode)
+
+| Shortcut                | Comment                                     |
+|-------------------------|---------------------------------------------|
+| `Ctrl`+`F`              | Move Forward                                |
+| `Ctrl`+`B`              | Move Backward                               |
+| `Ctrl`+`N`              | Move to next line                           |
+| `Ctrl`+`P`              | Move to previous line                       |
+| `Ctrl`+`A`              | Move to the first candicate in current line |
+| `Ctrl`+`E`              | Move to the last candicate in current line  |
+| `Tab` / `Enter`         | Use the word on cursor to complete          |
+| `Ctrl`+`C` / `Ctrl`+`G` | Exit Complete Select Mode                   |
+| Other                   | Exit Complete Select Mode                   |
+
+# Tested with
+
+| Environment                   | $TERM  |
+|-------------------------------|--------|
+| Mac OS X iTerm2               | xterm  |
+| Mac OS X default Terminal.app | xterm  |
+| Mac OS X iTerm2 Screen        | screen |
+| Mac OS X iTerm2 Tmux          | screen |
+| Ubuntu Server 14.04 LTS       | linux  |
+| Centos 7                      | linux  |
+| Windows 10                    | -      |
+
+### Notice:
+* `Ctrl`+`A` is not working in `screen` because it used as a control command by default
+
+If you test it otherwhere, whether it works fine or not, please let me know!
+
+## Who is using Readline
+
+* [cockroachdb/cockroach](https://github.com/cockroachdb/cockroach)
+* [youtube/doorman](https://github.com/youtube/doorman)
+* [bom-d-van/harp](https://github.com/bom-d-van/harp)
+* [abiosoft/ishell](https://github.com/abiosoft/ishell)
+* [robertkrimen/otto](https://github.com/robertkrimen/otto)
+* [Netflix/hal-9001](https://github.com/Netflix/hal-9001)
+* [docker/go-p9p](https://github.com/docker/go-p9p)
+
+# Feedback
+
+If you have any questions, please submit a github issue and any pull requests is welcomed :)
+
+* [https://twitter.com/chzyer](https://twitter.com/chzyer)  
+* [http://weibo.com/2145262190](http://weibo.com/2145262190)  
+
+
+# Backers
+
+Love Readline? Help me keep it alive by donating funds to cover project expenses!<br />
+[[Become a backer](https://opencollective.com/readline#backer)]
+
+<a href="https://opencollective.com/readline/backer/0/website" target="_blank"><img src="https://opencollective.com/readline/backer/0/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/1/website" target="_blank"><img src="https://opencollective.com/readline/backer/1/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/2/website" target="_blank"><img src="https://opencollective.com/readline/backer/2/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/3/website" target="_blank"><img src="https://opencollective.com/readline/backer/3/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/4/website" target="_blank"><img src="https://opencollective.com/readline/backer/4/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/5/website" target="_blank"><img src="https://opencollective.com/readline/backer/5/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/6/website" target="_blank"><img src="https://opencollective.com/readline/backer/6/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/7/website" target="_blank"><img src="https://opencollective.com/readline/backer/7/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/8/website" target="_blank"><img src="https://opencollective.com/readline/backer/8/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/9/website" target="_blank"><img src="https://opencollective.com/readline/backer/9/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/10/website" target="_blank"><img src="https://opencollective.com/readline/backer/10/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/11/website" target="_blank"><img src="https://opencollective.com/readline/backer/11/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/12/website" target="_blank"><img src="https://opencollective.com/readline/backer/12/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/13/website" target="_blank"><img src="https://opencollective.com/readline/backer/13/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/14/website" target="_blank"><img src="https://opencollective.com/readline/backer/14/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/15/website" target="_blank"><img src="https://opencollective.com/readline/backer/15/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/16/website" target="_blank"><img src="https://opencollective.com/readline/backer/16/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/17/website" target="_blank"><img src="https://opencollective.com/readline/backer/17/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/18/website" target="_blank"><img src="https://opencollective.com/readline/backer/18/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/19/website" target="_blank"><img src="https://opencollective.com/readline/backer/19/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/20/website" target="_blank"><img src="https://opencollective.com/readline/backer/20/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/21/website" target="_blank"><img src="https://opencollective.com/readline/backer/21/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/22/website" target="_blank"><img src="https://opencollective.com/readline/backer/22/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/23/website" target="_blank"><img src="https://opencollective.com/readline/backer/23/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/24/website" target="_blank"><img src="https://opencollective.com/readline/backer/24/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/25/website" target="_blank"><img src="https://opencollective.com/readline/backer/25/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/26/website" target="_blank"><img src="https://opencollective.com/readline/backer/26/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/27/website" target="_blank"><img src="https://opencollective.com/readline/backer/27/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/28/website" target="_blank"><img src="https://opencollective.com/readline/backer/28/avatar.svg"></a>
+<a href="https://opencollective.com/readline/backer/29/website" target="_blank"><img src="https://opencollective.com/readline/backer/29/avatar.svg"></a>
+
+
+# Sponsors
+
+Become a sponsor and get your logo here on our Github page. [[Become a sponsor](https://opencollective.com/readline#sponsor)]
+
+<a href="https://opencollective.com/readline/sponsor/0/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/1/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/2/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/3/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/4/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/5/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/6/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/7/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/8/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/9/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/9/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/10/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/10/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/11/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/11/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/12/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/12/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/13/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/13/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/14/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/14/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/15/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/15/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/16/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/16/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/17/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/17/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/18/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/18/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/19/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/19/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/20/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/20/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/21/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/21/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/22/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/22/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/23/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/23/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/24/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/24/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/25/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/25/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/26/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/26/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/27/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/27/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/28/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/28/avatar.svg"></a>
+<a href="https://opencollective.com/readline/sponsor/29/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/29/avatar.svg"></a>
+
diff --git a/vendor/gopkg.in/readline.v1/ansi_windows.go b/vendor/gopkg.in/readline.v1/ansi_windows.go
new file mode 100644
index 0000000..da106b5
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/ansi_windows.go
@@ -0,0 +1,246 @@
+// +build windows
+
+package readline
+
+import (
+	"bufio"
+	"io"
+	"strconv"
+	"strings"
+	"sync"
+	"unicode/utf8"
+	"unsafe"
+)
+
+const (
+	_                = uint16(0)
+	COLOR_FBLUE      = 0x0001
+	COLOR_FGREEN     = 0x0002
+	COLOR_FRED       = 0x0004
+	COLOR_FINTENSITY = 0x0008
+
+	COLOR_BBLUE      = 0x0010
+	COLOR_BGREEN     = 0x0020
+	COLOR_BRED       = 0x0040
+	COLOR_BINTENSITY = 0x0080
+
+	COMMON_LVB_UNDERSCORE = 0x8000
+)
+
+var ColorTableFg = []word{
+	0,                                       // 30: Black
+	COLOR_FRED,                              // 31: Red
+	COLOR_FGREEN,                            // 32: Green
+	COLOR_FRED | COLOR_FGREEN,               // 33: Yellow
+	COLOR_FBLUE,                             // 34: Blue
+	COLOR_FRED | COLOR_FBLUE,                // 35: Magenta
+	COLOR_FGREEN | COLOR_FBLUE,              // 36: Cyan
+	COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White
+}
+
+var ColorTableBg = []word{
+	0,                                       // 40: Black
+	COLOR_BRED,                              // 41: Red
+	COLOR_BGREEN,                            // 42: Green
+	COLOR_BRED | COLOR_BGREEN,               // 43: Yellow
+	COLOR_BBLUE,                             // 44: Blue
+	COLOR_BRED | COLOR_BBLUE,                // 45: Magenta
+	COLOR_BGREEN | COLOR_BBLUE,              // 46: Cyan
+	COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White
+}
+
+type ANSIWriter struct {
+	target io.Writer
+	wg     sync.WaitGroup
+	ctx    *ANSIWriterCtx
+	sync.Mutex
+}
+
+func NewANSIWriter(w io.Writer) *ANSIWriter {
+	a := &ANSIWriter{
+		target: w,
+		ctx:    NewANSIWriterCtx(w),
+	}
+	return a
+}
+
+func (a *ANSIWriter) Close() error {
+	a.wg.Wait()
+	return nil
+}
+
+type ANSIWriterCtx struct {
+	isEsc     bool
+	isEscSeq  bool
+	arg       []string
+	target    *bufio.Writer
+	wantFlush bool
+}
+
+func NewANSIWriterCtx(target io.Writer) *ANSIWriterCtx {
+	return &ANSIWriterCtx{
+		target: bufio.NewWriter(target),
+	}
+}
+
+func (a *ANSIWriterCtx) Flush() {
+	a.target.Flush()
+}
+
+func (a *ANSIWriterCtx) process(r rune) bool {
+	if a.wantFlush {
+		if r == 0 || r == CharEsc {
+			a.wantFlush = false
+			a.target.Flush()
+		}
+	}
+	if a.isEscSeq {
+		a.isEscSeq = a.ioloopEscSeq(a.target, r, &a.arg)
+		return true
+	}
+
+	switch r {
+	case CharEsc:
+		a.isEsc = true
+	case '[':
+		if a.isEsc {
+			a.arg = nil
+			a.isEscSeq = true
+			a.isEsc = false
+			break
+		}
+		fallthrough
+	default:
+		a.target.WriteRune(r)
+		a.wantFlush = true
+	}
+	return true
+}
+
+func (a *ANSIWriterCtx) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) bool {
+	arg := *argptr
+	var err error
+
+	if r >= 'A' && r <= 'D' {
+		count := short(GetInt(arg, 1))
+		info, err := GetConsoleScreenBufferInfo()
+		if err != nil {
+			return false
+		}
+		switch r {
+		case 'A': // up
+			info.dwCursorPosition.y -= count
+		case 'B': // down
+			info.dwCursorPosition.y += count
+		case 'C': // right
+			info.dwCursorPosition.x += count
+		case 'D': // left
+			info.dwCursorPosition.x -= count
+		}
+		SetConsoleCursorPosition(&info.dwCursorPosition)
+		return false
+	}
+
+	switch r {
+	case 'J':
+		killLines()
+	case 'K':
+		eraseLine()
+	case 'm':
+		color := word(0)
+		for _, item := range arg {
+			var c int
+			c, err = strconv.Atoi(item)
+			if err != nil {
+				w.WriteString("[" + strings.Join(arg, ";") + "m")
+				break
+			}
+			if c >= 30 && c < 40 {
+				color ^= COLOR_FINTENSITY
+				color |= ColorTableFg[c-30]
+			} else if c >= 40 && c < 50 {
+				color ^= COLOR_BINTENSITY
+				color |= ColorTableBg[c-40]
+			} else if c == 4 {
+				color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7]
+			} else { // unknown code treat as reset
+				color = ColorTableFg[7]
+			}
+		}
+		if err != nil {
+			break
+		}
+		kernel.SetConsoleTextAttribute(stdout, uintptr(color))
+	case '\007': // set title
+	case ';':
+		if len(arg) == 0 || arg[len(arg)-1] != "" {
+			arg = append(arg, "")
+			*argptr = arg
+		}
+		return true
+	default:
+		if len(arg) == 0 {
+			arg = append(arg, "")
+		}
+		arg[len(arg)-1] += string(r)
+		*argptr = arg
+		return true
+	}
+	*argptr = nil
+	return false
+}
+
+func (a *ANSIWriter) Write(b []byte) (int, error) {
+	a.Lock()
+	defer a.Unlock()
+
+	off := 0
+	for len(b) > off {
+		r, size := utf8.DecodeRune(b[off:])
+		if size == 0 {
+			return off, io.ErrShortWrite
+		}
+		off += size
+		a.ctx.process(r)
+	}
+	a.ctx.Flush()
+	return off, nil
+}
+
+func killLines() error {
+	sbi, err := GetConsoleScreenBufferInfo()
+	if err != nil {
+		return err
+	}
+
+	size := (sbi.dwCursorPosition.y - sbi.dwSize.y) * sbi.dwSize.x
+	size += sbi.dwCursorPosition.x
+
+	var written int
+	kernel.FillConsoleOutputAttribute(stdout, uintptr(ColorTableFg[7]),
+		uintptr(size),
+		sbi.dwCursorPosition.ptr(),
+		uintptr(unsafe.Pointer(&written)),
+	)
+	return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
+		uintptr(size),
+		sbi.dwCursorPosition.ptr(),
+		uintptr(unsafe.Pointer(&written)),
+	)
+}
+
+func eraseLine() error {
+	sbi, err := GetConsoleScreenBufferInfo()
+	if err != nil {
+		return err
+	}
+
+	size := sbi.dwSize.x
+	sbi.dwCursorPosition.x = 0
+	var written int
+	return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
+		uintptr(size),
+		sbi.dwCursorPosition.ptr(),
+		uintptr(unsafe.Pointer(&written)),
+	)
+}
diff --git a/vendor/gopkg.in/readline.v1/char.go b/vendor/gopkg.in/readline.v1/char.go
new file mode 100644
index 0000000..e269640
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/char.go
@@ -0,0 +1,36 @@
+package readline
+
+const (
+	CharLineStart = 1
+	CharBackward  = 2
+	CharInterrupt = 3
+	CharDelete    = 4
+	CharLineEnd   = 5
+	CharForward   = 6
+	CharBell      = 7
+	CharCtrlH     = 8
+	CharTab       = 9
+	CharCtrlJ     = 10
+	CharKill      = 11
+	CharCtrlL     = 12
+	CharEnter     = 13
+	CharNext      = 14
+	CharPrev      = 16
+	CharBckSearch = 18
+	CharFwdSearch = 19
+	CharTranspose = 20
+	CharCtrlU     = 21
+	CharCtrlW     = 23
+	CharCtrlZ     = 26
+	CharEsc       = 27
+	CharEscapeEx  = 91
+	CharBackspace = 127
+)
+
+const (
+	MetaBackward rune = -iota - 1
+	MetaForward
+	MetaDelete
+	MetaBackspace
+	MetaTranspose
+)
diff --git a/vendor/gopkg.in/readline.v1/complete.go b/vendor/gopkg.in/readline.v1/complete.go
new file mode 100644
index 0000000..ef019f2
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/complete.go
@@ -0,0 +1,273 @@
+package readline
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+)
+
+type AutoCompleter interface {
+	// Readline will pass the whole line and current offset to it
+	// Completer need to pass all the candidates, and how long they shared the same characters in line
+	// Example:
+	//   [go, git, git-shell, grep]
+	//   Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
+	//   Do("gi", 2) => ["t", "t-shell"], 2
+	//   Do("git", 3) => ["", "-shell"], 3
+	Do(line []rune, pos int) (newLine [][]rune, length int)
+}
+
+type opCompleter struct {
+	w     io.Writer
+	op    *Operation
+	width int
+
+	inCompleteMode  bool
+	inSelectMode    bool
+	candidate       [][]rune
+	candidateSource []rune
+	candidateOff    int
+	candidateChoise int
+	candidateColNum int
+}
+
+func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
+	return &opCompleter{
+		w:     w,
+		op:    op,
+		width: width,
+	}
+}
+
+func (o *opCompleter) doSelect() {
+	if len(o.candidate) == 1 {
+		o.op.buf.WriteRunes(o.candidate[0])
+		o.ExitCompleteMode(false)
+		return
+	}
+	o.nextCandidate(1)
+	o.CompleteRefresh()
+}
+
+func (o *opCompleter) nextCandidate(i int) {
+	o.candidateChoise += i
+	o.candidateChoise = o.candidateChoise % len(o.candidate)
+	if o.candidateChoise < 0 {
+		o.candidateChoise = len(o.candidate) + o.candidateChoise
+	}
+}
+
+func (o *opCompleter) OnComplete() {
+	if o.IsInCompleteSelectMode() {
+		o.doSelect()
+		return
+	}
+
+	buf := o.op.buf
+	rs := buf.Runes()
+
+	if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
+		o.EnterCompleteSelectMode()
+		o.doSelect()
+		return
+	}
+
+	o.ExitCompleteSelectMode()
+	o.candidateSource = rs
+	newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
+	if len(newLines) == 0 {
+		o.ExitCompleteMode(false)
+		return
+	}
+
+	// only Aggregate candidates in non-complete mode
+	if !o.IsInCompleteMode() {
+		if len(newLines) == 1 {
+			buf.WriteRunes(newLines[0])
+			o.ExitCompleteMode(false)
+			return
+		}
+
+		same, size := runes.Aggregate(newLines)
+		if size > 0 {
+			buf.WriteRunes(same)
+			o.ExitCompleteMode(false)
+			return
+		}
+	}
+
+	o.EnterCompleteMode(offset, newLines)
+}
+
+func (o *opCompleter) IsInCompleteSelectMode() bool {
+	return o.inSelectMode
+}
+
+func (o *opCompleter) IsInCompleteMode() bool {
+	return o.inCompleteMode
+}
+
+func (o *opCompleter) HandleCompleteSelect(r rune) bool {
+	next := true
+	switch r {
+	case CharEnter, CharCtrlJ:
+		next = false
+		o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise])
+		o.ExitCompleteMode(false)
+	case CharLineStart:
+		num := o.candidateChoise % o.candidateColNum
+		o.nextCandidate(-num)
+	case CharLineEnd:
+		num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1
+		o.candidateChoise += num
+		if o.candidateChoise >= len(o.candidate) {
+			o.candidateChoise = len(o.candidate) - 1
+		}
+	case CharBackspace:
+		o.ExitCompleteSelectMode()
+		next = false
+	case CharTab, CharForward:
+		o.doSelect()
+	case CharBell, CharInterrupt:
+		o.ExitCompleteMode(true)
+		next = false
+	case CharNext:
+		tmpChoise := o.candidateChoise + o.candidateColNum
+		if tmpChoise >= o.getMatrixSize() {
+			tmpChoise -= o.getMatrixSize()
+		} else if tmpChoise >= len(o.candidate) {
+			tmpChoise += o.candidateColNum
+			tmpChoise -= o.getMatrixSize()
+		}
+		o.candidateChoise = tmpChoise
+	case CharBackward:
+		o.nextCandidate(-1)
+	case CharPrev:
+		tmpChoise := o.candidateChoise - o.candidateColNum
+		if tmpChoise < 0 {
+			tmpChoise += o.getMatrixSize()
+			if tmpChoise >= len(o.candidate) {
+				tmpChoise -= o.candidateColNum
+			}
+		}
+		o.candidateChoise = tmpChoise
+	default:
+		next = false
+		o.ExitCompleteSelectMode()
+	}
+	if next {
+		o.CompleteRefresh()
+		return true
+	}
+	return false
+}
+
+func (o *opCompleter) getMatrixSize() int {
+	line := len(o.candidate) / o.candidateColNum
+	if len(o.candidate)%o.candidateColNum != 0 {
+		line++
+	}
+	return line * o.candidateColNum
+}
+
+func (o *opCompleter) OnWidthChange(newWidth int) {
+	o.width = newWidth
+}
+
+func (o *opCompleter) CompleteRefresh() {
+	if !o.inCompleteMode {
+		return
+	}
+	lineCnt := o.op.buf.CursorLineCount()
+	colWidth := 0
+	for _, c := range o.candidate {
+		w := runes.WidthAll(c)
+		if w > colWidth {
+			colWidth = w
+		}
+	}
+	colWidth += o.candidateOff + 1
+	same := o.op.buf.RuneSlice(-o.candidateOff)
+
+	// -1 to avoid reach the end of line
+	width := o.width - 1
+	colNum := width / colWidth
+	colWidth += (width - (colWidth * colNum)) / colNum
+
+	o.candidateColNum = colNum
+	buf := bufio.NewWriter(o.w)
+	buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
+
+	colIdx := 0
+	lines := 1
+	buf.WriteString("\033[J")
+	for idx, c := range o.candidate {
+		inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode()
+		if inSelect {
+			buf.WriteString("\033[30;47m")
+		}
+		buf.WriteString(string(same))
+		buf.WriteString(string(c))
+		buf.Write(bytes.Repeat([]byte(" "), colWidth-len(c)))
+
+		if inSelect {
+			buf.WriteString("\033[0m")
+		}
+
+		colIdx++
+		if colIdx == colNum {
+			buf.WriteString("\n")
+			lines++
+			colIdx = 0
+		}
+	}
+
+	// move back
+	fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines)
+	fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen())
+	buf.Flush()
+}
+
+func (o *opCompleter) aggCandidate(candidate [][]rune) int {
+	offset := 0
+	for i := 0; i < len(candidate[0]); i++ {
+		for j := 0; j < len(candidate)-1; j++ {
+			if i > len(candidate[j]) {
+				goto aggregate
+			}
+			if candidate[j][i] != candidate[j+1][i] {
+				goto aggregate
+			}
+		}
+		offset = i
+	}
+aggregate:
+	return offset
+}
+
+func (o *opCompleter) EnterCompleteSelectMode() {
+	o.inSelectMode = true
+	o.candidateChoise = -1
+	o.CompleteRefresh()
+}
+
+func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {
+	o.inCompleteMode = true
+	o.candidate = candidate
+	o.candidateOff = offset
+	o.CompleteRefresh()
+}
+
+func (o *opCompleter) ExitCompleteSelectMode() {
+	o.inSelectMode = false
+	o.candidate = nil
+	o.candidateChoise = -1
+	o.candidateOff = -1
+	o.candidateSource = nil
+}
+
+func (o *opCompleter) ExitCompleteMode(revent bool) {
+	o.inCompleteMode = false
+	o.ExitCompleteSelectMode()
+}
diff --git a/vendor/gopkg.in/readline.v1/complete_helper.go b/vendor/gopkg.in/readline.v1/complete_helper.go
new file mode 100644
index 0000000..58d7248
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/complete_helper.go
@@ -0,0 +1,165 @@
+package readline
+
+import (
+	"bytes"
+	"strings"
+)
+
+// Caller type for dynamic completion
+type DynamicCompleteFunc func(string) []string
+
+type PrefixCompleterInterface interface {
+	Print(prefix string, level int, buf *bytes.Buffer)
+	Do(line []rune, pos int) (newLine [][]rune, length int)
+	GetName() []rune
+	GetChildren() []PrefixCompleterInterface
+	SetChildren(children []PrefixCompleterInterface)
+}
+
+type DynamicPrefixCompleterInterface interface {
+	PrefixCompleterInterface
+	IsDynamic() bool
+	GetDynamicNames(line []rune) [][]rune
+}
+
+type PrefixCompleter struct {
+	Name     []rune
+	Dynamic  bool
+	Callback DynamicCompleteFunc
+	Children []PrefixCompleterInterface
+}
+
+func (p *PrefixCompleter) Tree(prefix string) string {
+	buf := bytes.NewBuffer(nil)
+	p.Print(prefix, 0, buf)
+	return buf.String()
+}
+
+func Print(p PrefixCompleterInterface, prefix string, level int, buf *bytes.Buffer) {
+	if strings.TrimSpace(string(p.GetName())) != "" {
+		buf.WriteString(prefix)
+		if level > 0 {
+			buf.WriteString("?")
+			buf.WriteString(strings.Repeat("?", (level*4)-2))
+			buf.WriteString(" ")
+		}
+		buf.WriteString(string(p.GetName()) + "\n")
+		level++
+	}
+	for _, ch := range p.GetChildren() {
+		ch.Print(prefix, level, buf)
+	}
+}
+
+func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) {
+	Print(p, prefix, level, buf)
+}
+
+func (p *PrefixCompleter) IsDynamic() bool {
+	return p.Dynamic
+}
+
+func (p *PrefixCompleter) GetName() []rune {
+	return p.Name
+}
+
+func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune {
+	var names = [][]rune{}
+	for _, name := range p.Callback(string(line)) {
+		names = append(names, []rune(name+" "))
+	}
+	return names
+}
+
+func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface {
+	return p.Children
+}
+
+func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterface) {
+	p.Children = children
+}
+
+func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter {
+	return PcItem("", pc...)
+}
+
+func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter {
+	name += " "
+	return &PrefixCompleter{
+		Name:     []rune(name),
+		Dynamic:  false,
+		Children: pc,
+	}
+}
+
+func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterInterface) *PrefixCompleter {
+	return &PrefixCompleter{
+		Callback: callback,
+		Dynamic:  true,
+		Children: pc,
+	}
+}
+
+func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
+	return doInternal(p, line, pos, line)
+}
+
+func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, offset int) {
+	return doInternal(p, line, pos, line)
+}
+
+func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLine []rune) (newLine [][]rune, offset int) {
+	line = runes.TrimSpaceLeft(line[:pos])
+	goNext := false
+	var lineCompleter PrefixCompleterInterface
+	for _, child := range p.GetChildren() {
+		childNames := make([][]rune, 1)
+
+		childDynamic, ok := child.(DynamicPrefixCompleterInterface)
+		if ok && childDynamic.IsDynamic() {
+			childNames = childDynamic.GetDynamicNames(origLine)
+		} else {
+			childNames[0] = child.GetName()
+		}
+
+		for _, childName := range childNames {
+			if len(line) >= len(childName) {
+				if runes.HasPrefix(line, childName) {
+					if len(line) == len(childName) {
+						newLine = append(newLine, []rune{' '})
+					} else {
+						newLine = append(newLine, childName)
+					}
+					offset = len(childName)
+					lineCompleter = child
+					goNext = true
+				}
+			} else {
+				if runes.HasPrefix(childName, line) {
+					newLine = append(newLine, childName[len(line):])
+					offset = len(line)
+					lineCompleter = child
+				}
+			}
+		}
+	}
+
+	if len(newLine) != 1 {
+		return
+	}
+
+	tmpLine := make([]rune, 0, len(line))
+	for i := offset; i < len(line); i++ {
+		if line[i] == ' ' {
+			continue
+		}
+
+		tmpLine = append(tmpLine, line[i:]...)
+		return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine)
+	}
+
+	if goNext {
+		return doInternal(lineCompleter, nil, 0, origLine)
+	}
+	return
+}
diff --git a/vendor/gopkg.in/readline.v1/complete_segment.go b/vendor/gopkg.in/readline.v1/complete_segment.go
new file mode 100644
index 0000000..5ceadd8
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/complete_segment.go
@@ -0,0 +1,82 @@
+package readline
+
+type SegmentCompleter interface {
+	// a
+	// |- a1
+	// |--- a11
+	// |- a2
+	// b
+	// input:
+	//   DoTree([], 0) [a, b]
+	//   DoTree([a], 1) [a]
+	//   DoTree([a, ], 0) [a1, a2]
+	//   DoTree([a, a], 1) [a1, a2]
+	//   DoTree([a, a1], 2) [a1]
+	//   DoTree([a, a1, ], 0) [a11]
+	//   DoTree([a, a1, a], 1) [a11]
+	DoSegment([][]rune, int) [][]rune
+}
+
+type dumpSegmentCompleter struct {
+	f func([][]rune, int) [][]rune
+}
+
+func (d *dumpSegmentCompleter) DoSegment(segment [][]rune, n int) [][]rune {
+	return d.f(segment, n)
+}
+
+func SegmentFunc(f func([][]rune, int) [][]rune) AutoCompleter {
+	return &SegmentComplete{&dumpSegmentCompleter{f}}
+}
+
+func SegmentAutoComplete(completer SegmentCompleter) *SegmentComplete {
+	return &SegmentComplete{
+		SegmentCompleter: completer,
+	}
+}
+
+type SegmentComplete struct {
+	SegmentCompleter
+}
+
+func RetSegment(segments [][]rune, cands [][]rune, idx int) ([][]rune, int) {
+	ret := make([][]rune, 0, len(cands))
+	lastSegment := segments[len(segments)-1]
+	for _, cand := range cands {
+		if !runes.HasPrefix(cand, lastSegment) {
+			continue
+		}
+		ret = append(ret, cand[len(lastSegment):])
+	}
+	return ret, idx
+}
+
+func SplitSegment(line []rune, pos int) ([][]rune, int) {
+	segs := [][]rune{}
+	lastIdx := -1
+	line = line[:pos]
+	pos = 0
+	for idx, l := range line {
+		if l == ' ' {
+			pos = 0
+			segs = append(segs, line[lastIdx+1:idx])
+			lastIdx = idx
+		} else {
+			pos++
+		}
+	}
+	segs = append(segs, line[lastIdx+1:])
+	return segs, pos
+}
+
+func (c *SegmentComplete) Do(line []rune, pos int) (newLine [][]rune, offset int) {
+
+	segment, idx := SplitSegment(line, pos)
+
+	cands := c.DoSegment(segment, idx)
+	newLine, offset = RetSegment(segment, cands, idx)
+	for idx := range newLine {
+		newLine[idx] = append(newLine[idx], ' ')
+	}
+	return newLine, offset
+}
diff --git a/vendor/gopkg.in/readline.v1/complete_segment_test.go b/vendor/gopkg.in/readline.v1/complete_segment_test.go
new file mode 100644
index 0000000..73a828a
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/complete_segment_test.go
@@ -0,0 +1,167 @@
+package readline
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/chzyer/test"
+)
+
+func rs(s [][]rune) []string {
+	ret := make([]string, len(s))
+	for idx, ss := range s {
+		ret[idx] = string(ss)
+	}
+	return ret
+}
+
+func sr(s ...string) [][]rune {
+	ret := make([][]rune, len(s))
+	for idx, ss := range s {
+		ret[idx] = []rune(ss)
+	}
+	return ret
+}
+
+func TestRetSegment(t *testing.T) {
+	defer test.New(t)
+	// a
+	// |- a1
+	// |--- a11
+	// |--- a12
+	// |- a2
+	// |--- a21
+	// b
+	// add
+	// adddomain
+	ret := []struct {
+		Segments [][]rune
+		Cands    [][]rune
+		idx      int
+		Ret      [][]rune
+		pos      int
+	}{
+		{sr(""), sr("a", "b", "add", "adddomain"), 0, sr("a", "b", "add", "adddomain"), 0},
+		{sr("a"), sr("a", "add", "adddomain"), 1, sr("", "dd", "dddomain"), 1},
+		{sr("a", ""), sr("a1", "a2"), 0, sr("a1", "a2"), 0},
+		{sr("a", "a"), sr("a1", "a2"), 1, sr("1", "2"), 1},
+		{sr("a", "a1"), sr("a1"), 2, sr(""), 2},
+		{sr("add"), sr("add", "adddomain"), 2, sr("", "domain"), 2},
+	}
+	for idx, r := range ret {
+		ret, pos := RetSegment(r.Segments, r.Cands, r.idx)
+		test.Equal(ret, r.Ret, fmt.Errorf("%v", idx))
+		test.Equal(pos, r.pos, fmt.Errorf("%v", idx))
+	}
+}
+
+func TestSplitSegment(t *testing.T) {
+	defer test.New(t)
+	// a
+	// |- a1
+	// |--- a11
+	// |--- a12
+	// |- a2
+	// |--- a21
+	// b
+	ret := []struct {
+		Line     string
+		Pos      int
+		Segments [][]rune
+		Idx      int
+	}{
+		{"", 0, sr(""), 0},
+		{"a", 1, sr("a"), 1},
+		{"a ", 2, sr("a", ""), 0},
+		{"a a", 3, sr("a", "a"), 1},
+		{"a a1", 4, sr("a", "a1"), 2},
+		{"a a1 ", 5, sr("a", "a1", ""), 0},
+	}
+
+	for i, r := range ret {
+		ret, idx := SplitSegment([]rune(r.Line), r.Pos)
+		test.Equal(rs(ret), rs(r.Segments), fmt.Errorf("%v", i))
+		test.Equal(idx, r.Idx, fmt.Errorf("%v", i))
+	}
+}
+
+type Tree struct {
+	Name     string
+	Children []Tree
+}
+
+func TestSegmentCompleter(t *testing.T) {
+	defer test.New(t)
+
+	tree := Tree{"", []Tree{
+		{"a", []Tree{
+			{"a1", []Tree{
+				{"a11", nil},
+				{"a12", nil},
+			}},
+			{"a2", []Tree{
+				{"a21", nil},
+			}},
+		}},
+		{"b", nil},
+		{"route", []Tree{
+			{"add", nil},
+			{"adddomain", nil},
+		}},
+	}}
+	s := SegmentFunc(func(ret [][]rune, n int) [][]rune {
+		tree := tree
+	main:
+		for level := 0; level < len(ret)-1; {
+			name := string(ret[level])
+			for _, t := range tree.Children {
+				if t.Name == name {
+					tree = t
+					level++
+					continue main
+				}
+			}
+		}
+
+		ret = make([][]rune, len(tree.Children))
+		for idx, r := range tree.Children {
+			ret[idx] = []rune(r.Name)
+		}
+		return ret
+	})
+
+	// a
+	// |- a1
+	// |--- a11
+	// |--- a12
+	// |- a2
+	// |--- a21
+	// b
+	ret := []struct {
+		Line  string
+		Pos   int
+		Ret   [][]rune
+		Share int
+	}{
+		{"", 0, sr("a", "b", "route"), 0},
+		{"a", 1, sr(""), 1},
+		{"a ", 2, sr("a1", "a2"), 0},
+		{"a a", 3, sr("1", "2"), 1},
+		{"a a1", 4, sr(""), 2},
+		{"a a1 ", 5, sr("a11", "a12"), 0},
+		{"a a1 a", 6, sr("11", "12"), 1},
+		{"a a1 a1", 7, sr("1", "2"), 2},
+		{"a a1 a11", 8, sr(""), 3},
+		{"route add", 9, sr("", "domain"), 3},
+	}
+	for _, r := range ret {
+		for idx, rr := range r.Ret {
+			r.Ret[idx] = append(rr, ' ')
+		}
+	}
+	for i, r := range ret {
+		newLine, length := s.Do([]rune(r.Line), r.Pos)
+		test.Equal(rs(newLine), rs(r.Ret), fmt.Errorf("%v", i))
+		test.Equal(length, r.Share, fmt.Errorf("%v", i))
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/debug.go b/vendor/gopkg.in/readline.v1/debug.go
new file mode 100644
index 0000000..7878500
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/debug.go
@@ -0,0 +1,29 @@
+package readline
+
+import (
+	"container/list"
+	"fmt"
+	"os"
+	"time"
+)
+
+func sleep(n int) {
+	Debug(n)
+	time.Sleep(2000 * time.Millisecond)
+}
+
+// print a linked list to Debug()
+func debugList(l *list.List) {
+	idx := 0
+	for e := l.Front(); e != nil; e = e.Next() {
+		Debug(idx, fmt.Sprintf("%+v", e.Value))
+		idx++
+	}
+}
+
+// append log info to another file
+func Debug(o ...interface{}) {
+	f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+	fmt.Fprintln(f, o...)
+	f.Close()
+}
diff --git a/vendor/gopkg.in/readline.v1/doc.go b/vendor/gopkg.in/readline.v1/doc.go
new file mode 100644
index 0000000..656d6ca
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/doc.go
@@ -0,0 +1,20 @@
+// Readline is a pure go implementation for GNU-Readline kind library.
+//
+// WHY: Readline will support most of features which GNU Readline is supported, and provide a pure go environment and a MIT license.
+//
+// example:
+// 	rl, err := readline.New("> ")
+// 	if err != nil {
+// 		panic(err)
+// 	}
+// 	defer rl.Close()
+//
+// 	for {
+// 		line, err := rl.Readline()
+// 		if err != nil { // io.EOF
+// 			break
+// 		}
+// 		println(line)
+// 	}
+//
+package readline
diff --git a/vendor/gopkg.in/readline.v1/example/readline-demo/readline-demo.go b/vendor/gopkg.in/readline.v1/example/readline-demo/readline-demo.go
new file mode 100644
index 0000000..d80e59e
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-demo/readline-demo.go
@@ -0,0 +1,156 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/chzyer/readline"
+)
+
+func usage(w io.Writer) {
+	io.WriteString(w, "commands:\n")
+	io.WriteString(w, completer.Tree("    "))
+}
+
+// Function constructor - constructs new function for listing given directory
+func listFiles(path string) func(string) []string {
+	return func(line string) []string {
+		names := make([]string, 0)
+		files, _ := ioutil.ReadDir(path)
+		for _, f := range files {
+			names = append(names, f.Name())
+		}
+		return names
+	}
+}
+
+var completer = readline.NewPrefixCompleter(
+	readline.PcItem("mode",
+		readline.PcItem("vi"),
+		readline.PcItem("emacs"),
+	),
+	readline.PcItem("login"),
+	readline.PcItem("say",
+		readline.PcItemDynamic(listFiles("./"),
+			readline.PcItem("with",
+				readline.PcItem("following"),
+				readline.PcItem("items"),
+			),
+		),
+		readline.PcItem("hello"),
+		readline.PcItem("bye"),
+	),
+	readline.PcItem("setprompt"),
+	readline.PcItem("setpassword"),
+	readline.PcItem("bye"),
+	readline.PcItem("help"),
+	readline.PcItem("go",
+		readline.PcItem("build", readline.PcItem("-o"), readline.PcItem("-v")),
+		readline.PcItem("install",
+			readline.PcItem("-v"),
+			readline.PcItem("-vv"),
+			readline.PcItem("-vvv"),
+		),
+		readline.PcItem("test"),
+	),
+	readline.PcItem("sleep"),
+)
+
+func main() {
+	l, err := readline.NewEx(&readline.Config{
+		Prompt:          "\033[31m?\033[0m ",
+		HistoryFile:     "/tmp/readline.tmp",
+		AutoComplete:    completer,
+		InterruptPrompt: "^C",
+		EOFPrompt:       "exit",
+	})
+	if err != nil {
+		panic(err)
+	}
+	defer l.Close()
+
+	setPasswordCfg := l.GenPasswordConfig()
+	setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
+		l.SetPrompt(fmt.Sprintf("Enter password(%v): ", len(line)))
+		l.Refresh()
+		return nil, 0, false
+	})
+
+	log.SetOutput(l.Stderr())
+	for {
+		line, err := l.Readline()
+		if err == readline.ErrInterrupt {
+			if len(line) == 0 {
+				break
+			} else {
+				continue
+			}
+		} else if err == io.EOF {
+			break
+		}
+
+		line = strings.TrimSpace(line)
+		switch {
+		case strings.HasPrefix(line, "mode "):
+			switch line[5:] {
+			case "vi":
+				l.SetVimMode(true)
+			case "emacs":
+				l.SetVimMode(false)
+			default:
+				println("invalid mode:", line[5:])
+			}
+		case line == "mode":
+			if l.IsVimMode() {
+				println("current mode: vim")
+			} else {
+				println("current mode: emacs")
+			}
+		case line == "login":
+			pswd, err := l.ReadPassword("please enter your password: ")
+			if err != nil {
+				break
+			}
+			println("you enter:", strconv.Quote(string(pswd)))
+		case line == "help":
+			usage(l.Stderr())
+		case line == "setpassword":
+			pswd, err := l.ReadPasswordWithConfig(setPasswordCfg)
+			if err == nil {
+				println("you set:", strconv.Quote(string(pswd)))
+			}
+		case strings.HasPrefix(line, "setprompt"):
+			prompt := line[10:]
+			if prompt == "" {
+				log.Println("setprompt <prompt>")
+				break
+			}
+			l.SetPrompt(prompt)
+		case strings.HasPrefix(line, "say"):
+			line := strings.TrimSpace(line[3:])
+			if len(line) == 0 {
+				log.Println("say what?")
+				break
+			}
+			go func() {
+				for range time.Tick(time.Second) {
+					log.Println(line)
+				}
+			}()
+		case line == "bye":
+			goto exit
+		case line == "sleep":
+			log.Println("sleep 4 second")
+			time.Sleep(4 * time.Second)
+		case line == "":
+		default:
+			log.Println("you said:", strconv.Quote(line))
+		}
+	}
+exit:
+}
diff --git a/vendor/gopkg.in/readline.v1/example/readline-im/README.md b/vendor/gopkg.in/readline.v1/example/readline-im/README.md
new file mode 100644
index 0000000..2d29212
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-im/README.md
@@ -0,0 +1,3 @@
+# readline-im
+
+![readline-im](https://dl.dropboxusercontent.com/s/52hc7bo92g3pgi5/03F93B8D-9B4B-4D35-BBAA-22FBDAC7F299-26173-000164AA33980001.gif?dl=0)
diff --git a/vendor/gopkg.in/readline.v1/example/readline-im/readline-im.go b/vendor/gopkg.in/readline.v1/example/readline-im/readline-im.go
new file mode 100644
index 0000000..16803bd
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-im/readline-im.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+	"fmt"
+	"math/rand"
+	"time"
+
+	"github.com/chzyer/readline"
+)
+import "log"
+
+func main() {
+	rl, err := readline.NewEx(&readline.Config{
+		UniqueEditLine: true,
+	})
+	if err != nil {
+		panic(err)
+	}
+	defer rl.Close()
+
+	rl.SetPrompt("username: ")
+	username, err := rl.Readline()
+	if err != nil {
+		return
+	}
+	rl.ResetHistory()
+	log.SetOutput(rl.Stderr())
+
+	fmt.Fprintln(rl, "Hi,", username+"! My name is Dave.")
+	rl.SetPrompt(username + "> ")
+
+	done := make(chan struct{})
+	go func() {
+		rand.Seed(time.Now().Unix())
+	loop:
+		for {
+			select {
+			case <-time.After(time.Duration(rand.Intn(20)) * 100 * time.Millisecond):
+			case <-done:
+				break loop
+			}
+			log.Println("Dave:", "hello")
+		}
+		log.Println("Dave:", "bye")
+		done <- struct{}{}
+	}()
+
+	for {
+		ln := rl.Line()
+		if ln.CanContinue() {
+			continue
+		} else if ln.CanBreak() {
+			break
+		}
+		log.Println(username+":", ln.Line)
+	}
+	rl.Clean()
+	done <- struct{}{}
+	<-done
+}
diff --git a/vendor/gopkg.in/readline.v1/example/readline-multiline/readline-multiline.go b/vendor/gopkg.in/readline.v1/example/readline-multiline/readline-multiline.go
new file mode 100644
index 0000000..2192cf6
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-multiline/readline-multiline.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+	"strings"
+
+	"github.com/chzyer/readline"
+)
+
+func main() {
+	rl, err := readline.NewEx(&readline.Config{
+		Prompt:                 "> ",
+		HistoryFile:            "/tmp/readline-multiline",
+		DisableAutoSaveHistory: true,
+	})
+	if err != nil {
+		panic(err)
+	}
+	defer rl.Close()
+
+	var cmds []string
+	for {
+		line, err := rl.Readline()
+		if err != nil {
+			break
+		}
+		line = strings.TrimSpace(line)
+		if len(line) == 0 {
+			continue
+		}
+		cmds = append(cmds, line)
+		if !strings.HasSuffix(line, ";") {
+			rl.SetPrompt(">>> ")
+			continue
+		}
+		cmd := strings.Join(cmds, " ")
+		cmds = cmds[:0]
+		rl.SetPrompt("> ")
+		rl.SaveHistory(cmd)
+		println(cmd)
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/example/readline-pass-strength/readline-pass-strength.go b/vendor/gopkg.in/readline.v1/example/readline-pass-strength/readline-pass-strength.go
new file mode 100644
index 0000000..afcef45
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-pass-strength/readline-pass-strength.go
@@ -0,0 +1,102 @@
+// This is a small example using readline to read a password
+// and check it's strength while typing using the zxcvbn library.
+// Depending on the strength the prompt is colored nicely to indicate strength.
+//
+// This file is licensed under the WTFPL:
+//
+//         DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+//                     Version 2, December 2004
+//
+//  Copyright (C) 2004 Sam Hocevar <sa...@hocevar.net>
+//
+//  Everyone is permitted to copy and distribute verbatim or modified
+//  copies of this license document, and changing it is allowed as long
+//  as the name is changed.
+//
+//             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+//    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+//
+//   0. You just DO WHAT THE FUCK YOU WANT TO.
+package main
+
+import (
+	"fmt"
+
+	"github.com/chzyer/readline"
+	zxcvbn "github.com/nbutton23/zxcvbn-go"
+)
+
+const (
+	Cyan          = 36
+	Green         = 32
+	Magenta       = 35
+	Red           = 31
+	Yellow        = 33
+	BackgroundRed = 41
+)
+
+// Reset sequence
+var ColorResetEscape = "\033[0m"
+
+// ColorResetEscape translates a ANSI color number to a color escape.
+func ColorEscape(color int) string {
+	return fmt.Sprintf("\033[0;%dm", color)
+}
+
+// Colorize the msg using ANSI color escapes
+func Colorize(msg string, color int) string {
+	return ColorEscape(color) + msg + ColorResetEscape
+}
+
+func createStrengthPrompt(password []rune) string {
+	symbol, color := "", Red
+	strength := zxcvbn.PasswordStrength(string(password), nil)
+
+	switch {
+	case strength.Score <= 1:
+		symbol = "?"
+		color = Red
+	case strength.Score <= 2:
+		symbol = "?"
+		color = Magenta
+	case strength.Score <= 3:
+		symbol = "?"
+		color = Yellow
+	case strength.Score <= 4:
+		symbol = "?"
+		color = Green
+	}
+
+	prompt := Colorize(symbol, color)
+	if strength.Entropy > 0 {
+		entropy := fmt.Sprintf(" %3.0f", strength.Entropy)
+		prompt += Colorize(entropy, Cyan)
+	} else {
+		prompt += Colorize(" ENT", Cyan)
+	}
+
+	prompt += Colorize(" New Password: ", color)
+	return prompt
+}
+
+func main() {
+	rl, err := readline.New("")
+	if err != nil {
+		return
+	}
+	defer rl.Close()
+
+	setPasswordCfg := rl.GenPasswordConfig()
+	setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
+		rl.SetPrompt(createStrengthPrompt(line))
+		rl.Refresh()
+		return nil, 0, false
+	})
+
+	pswd, err := rl.ReadPasswordWithConfig(setPasswordCfg)
+	if err != nil {
+		return
+	}
+
+	fmt.Println("Your password was:", string(pswd))
+}
diff --git a/vendor/gopkg.in/readline.v1/example/readline-remote/readline-remote-client/client.go b/vendor/gopkg.in/readline.v1/example/readline-remote/readline-remote-client/client.go
new file mode 100644
index 0000000..3b7ff31
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-remote/readline-remote-client/client.go
@@ -0,0 +1,9 @@
+package main
+
+import "github.com/chzyer/readline"
+
+func main() {
+	if err := readline.DialRemote("tcp", ":12344"); err != nil {
+		println(err.Error())
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/example/readline-remote/readline-remote-server/server.go b/vendor/gopkg.in/readline.v1/example/readline-remote/readline-remote-server/server.go
new file mode 100644
index 0000000..38abc7d
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/example/readline-remote/readline-remote-server/server.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/chzyer/readline"
+)
+
+func main() {
+	cfg := &readline.Config{
+		Prompt: "readline-remote: ",
+	}
+	handleFunc := func(rl *readline.Instance) {
+		for {
+			line, err := rl.Readline()
+			if err != nil {
+				break
+			}
+			fmt.Fprintln(rl.Stdout(), "receive:"+line)
+		}
+	}
+	err := readline.ListenRemote("tcp", ":12344", cfg, handleFunc)
+	if err != nil {
+		println(err.Error())
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/history.go b/vendor/gopkg.in/readline.v1/history.go
new file mode 100644
index 0000000..d71c44b
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/history.go
@@ -0,0 +1,296 @@
+package readline
+
+import (
+	"bufio"
+	"container/list"
+	"fmt"
+	"os"
+	"strings"
+)
+
+type hisItem struct {
+	Source  []rune
+	Version int64
+	Tmp     []rune
+}
+
+func (h *hisItem) Clean() {
+	h.Source = nil
+	h.Tmp = nil
+}
+
+type opHistory struct {
+	cfg        *Config
+	history    *list.List
+	historyVer int64
+	current    *list.Element
+	fd         *os.File
+}
+
+func newOpHistory(cfg *Config) (o *opHistory) {
+	o = &opHistory{
+		cfg:     cfg,
+		history: list.New(),
+	}
+	return o
+}
+
+func (o *opHistory) Reset() {
+	o.history = list.New()
+	o.current = nil
+}
+
+func (o *opHistory) IsHistoryClosed() bool {
+	return o.fd.Fd() == ^(uintptr(0))
+}
+
+func (o *opHistory) Init() {
+	if o.IsHistoryClosed() {
+		o.initHistory()
+	}
+}
+
+func (o *opHistory) initHistory() {
+	if o.cfg.HistoryFile != "" {
+		o.historyUpdatePath(o.cfg.HistoryFile)
+	}
+}
+
+// only called by newOpHistory
+func (o *opHistory) historyUpdatePath(path string) {
+	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
+	if err != nil {
+		return
+	}
+	o.fd = f
+	r := bufio.NewReader(o.fd)
+	total := 0
+	for ; ; total++ {
+		line, err := r.ReadString('\n')
+		if err != nil {
+			break
+		}
+		// ignore the empty line
+		line = strings.TrimSpace(line)
+		if len(line) == 0 {
+			continue
+		}
+		o.Push([]rune(line))
+		o.Compact()
+	}
+	if total > o.cfg.HistoryLimit {
+		o.Rewrite()
+	}
+	o.historyVer++
+	o.Push(nil)
+	return
+}
+
+func (o *opHistory) Compact() {
+	for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
+		o.history.Remove(o.history.Front())
+	}
+}
+
+func (o *opHistory) Rewrite() {
+	if o.cfg.HistoryFile == "" {
+		return
+	}
+
+	tmpFile := o.cfg.HistoryFile + ".tmp"
+	fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
+	if err != nil {
+		return
+	}
+
+	buf := bufio.NewWriter(fd)
+	for elem := o.history.Front(); elem != nil; elem = elem.Next() {
+		buf.WriteString(string(elem.Value.(*hisItem).Source))
+	}
+	buf.Flush()
+
+	// replace history file
+	if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil {
+		fd.Close()
+		return
+	}
+
+	if o.fd != nil {
+		o.fd.Close()
+	}
+	// fd is write only, just satisfy what we need.
+	o.fd = fd
+}
+
+func (o *opHistory) Close() {
+	if o.fd != nil {
+		o.fd.Close()
+	}
+}
+
+func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
+	for elem := o.current; elem != nil; elem = elem.Prev() {
+		item := o.showItem(elem.Value)
+		if isNewSearch {
+			start += len(rs)
+		}
+		if elem == o.current {
+			if len(item) >= start {
+				item = item[:start]
+			}
+		}
+		idx := runes.IndexAllBck(item, rs)
+		if idx < 0 {
+			continue
+		}
+		return idx, elem
+	}
+	return -1, nil
+}
+
+func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
+	for elem := o.current; elem != nil; elem = elem.Next() {
+		item := o.showItem(elem.Value)
+		if isNewSearch {
+			start -= len(rs)
+			if start < 0 {
+				start = 0
+			}
+		}
+		if elem == o.current {
+			if len(item)-1 >= start {
+				item = item[start:]
+			} else {
+				continue
+			}
+		}
+		idx := runes.IndexAll(item, rs)
+		if idx < 0 {
+			continue
+		}
+		if elem == o.current {
+			idx += start
+		}
+		return idx, elem
+	}
+	return -1, nil
+}
+
+func (o *opHistory) showItem(obj interface{}) []rune {
+	item := obj.(*hisItem)
+	if item.Version == o.historyVer {
+		return item.Tmp
+	}
+	return item.Source
+}
+
+func (o *opHistory) Prev() []rune {
+	if o.current == nil {
+		return nil
+	}
+	current := o.current.Prev()
+	if current == nil {
+		return nil
+	}
+	o.current = current
+	return runes.Copy(o.showItem(current.Value))
+}
+
+func (o *opHistory) Next() ([]rune, bool) {
+	if o.current == nil {
+		return nil, false
+	}
+	current := o.current.Next()
+	if current == nil {
+		return nil, false
+	}
+
+	o.current = current
+	return runes.Copy(o.showItem(current.Value)), true
+}
+
+func (o *opHistory) debug() {
+	Debug("-------")
+	for item := o.history.Front(); item != nil; item = item.Next() {
+		Debug(fmt.Sprintf("%+v", item.Value))
+	}
+}
+
+// save history
+func (o *opHistory) New(current []rune) (err error) {
+	current = runes.Copy(current)
+
+	// if just use last command without modify
+	// just clean lastest history
+	if back := o.history.Back(); back != nil {
+		prev := back.Prev()
+		if prev != nil {
+			if runes.Equal(current, prev.Value.(*hisItem).Source) {
+				o.current = o.history.Back()
+				o.current.Value.(*hisItem).Clean()
+				o.historyVer++
+				return nil
+			}
+		}
+	}
+
+	if len(current) == 0 {
+		o.current = o.history.Back()
+		if o.current != nil {
+			o.current.Value.(*hisItem).Clean()
+			o.historyVer++
+			return nil
+		}
+	}
+
+	if o.current != o.history.Back() {
+		// move history item to current command
+		currentItem := o.current.Value.(*hisItem)
+		// set current to last item
+		o.current = o.history.Back()
+
+		current = runes.Copy(currentItem.Tmp)
+	}
+
+	// err only can be a IO error, just report
+	err = o.Update(current, true)
+
+	// push a new one to commit current command
+	o.historyVer++
+	o.Push(nil)
+	return
+}
+
+func (o *opHistory) Revert() {
+	o.historyVer++
+	o.current = o.history.Back()
+}
+
+func (o *opHistory) Update(s []rune, commit bool) (err error) {
+	s = runes.Copy(s)
+	if o.current == nil {
+		o.Push(s)
+		o.Compact()
+		return
+	}
+	r := o.current.Value.(*hisItem)
+	r.Version = o.historyVer
+	if commit {
+		r.Source = s
+		if o.fd != nil {
+			// just report the error
+			_, err = o.fd.Write([]byte(string(r.Source) + "\n"))
+		}
+	} else {
+		r.Tmp = append(r.Tmp[:0], s...)
+	}
+	o.current.Value = r
+	o.Compact()
+	return
+}
+
+func (o *opHistory) Push(s []rune) {
+	s = runes.Copy(s)
+	elem := o.history.PushBack(&hisItem{Source: s})
+	o.current = elem
+}
diff --git a/vendor/gopkg.in/readline.v1/operation.go b/vendor/gopkg.in/readline.v1/operation.go
new file mode 100644
index 0000000..e83485a
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/operation.go
@@ -0,0 +1,479 @@
+package readline
+
+import (
+	"errors"
+	"io"
+)
+
+var (
+	ErrInterrupt = errors.New("Interrupt")
+)
+
+type InterruptError struct {
+	Line []rune
+}
+
+func (*InterruptError) Error() string {
+	return "Interrupted"
+}
+
+type Operation struct {
+	cfg     *Config
+	t       *Terminal
+	buf     *RuneBuffer
+	outchan chan []rune
+	errchan chan error
+	w       io.Writer
+
+	history *opHistory
+	*opSearch
+	*opCompleter
+	*opPassword
+	*opVim
+}
+
+type wrapWriter struct {
+	r      *Operation
+	t      *Terminal
+	target io.Writer
+}
+
+func (w *wrapWriter) Write(b []byte) (int, error) {
+	if !w.t.IsReading() {
+		return w.target.Write(b)
+	}
+
+	var (
+		n   int
+		err error
+	)
+	w.r.buf.Refresh(func() {
+		n, err = w.target.Write(b)
+	})
+
+	if w.r.IsSearchMode() {
+		w.r.SearchRefresh(-1)
+	}
+	if w.r.IsInCompleteMode() {
+		w.r.CompleteRefresh()
+	}
+	return n, err
+}
+
+func NewOperation(t *Terminal, cfg *Config) *Operation {
+	width := cfg.FuncGetWidth()
+	op := &Operation{
+		t:       t,
+		buf:     NewRuneBuffer(t, cfg.Prompt, cfg, width),
+		outchan: make(chan []rune),
+		errchan: make(chan error),
+	}
+	op.w = op.buf.w
+	op.SetConfig(cfg)
+	op.opVim = newVimMode(op)
+	op.opCompleter = newOpCompleter(op.buf.w, op, width)
+	op.opPassword = newOpPassword(op)
+	op.cfg.FuncOnWidthChanged(func() {
+		newWidth := cfg.FuncGetWidth()
+		op.opCompleter.OnWidthChange(newWidth)
+		op.opSearch.OnWidthChange(newWidth)
+		op.buf.OnWidthChange(newWidth)
+	})
+	go op.ioloop()
+	return op
+}
+
+func (o *Operation) SetPrompt(s string) {
+	o.buf.SetPrompt(s)
+}
+
+func (o *Operation) SetMaskRune(r rune) {
+	o.buf.SetMask(r)
+}
+
+func (o *Operation) ioloop() {
+	for {
+		keepInSearchMode := false
+		keepInCompleteMode := false
+		r := o.t.ReadRune()
+		if r == 0 { // io.EOF
+			if o.buf.Len() == 0 {
+				o.buf.Clean()
+				select {
+				case o.errchan <- io.EOF:
+				}
+				break
+			} else {
+				// if stdin got io.EOF and there is something left in buffer,
+				// let's flush them by sending CharEnter.
+				// And we will got io.EOF int next loop.
+				r = CharEnter
+			}
+		}
+		isUpdateHistory := true
+
+		if o.IsInCompleteSelectMode() {
+			keepInCompleteMode = o.HandleCompleteSelect(r)
+			if keepInCompleteMode {
+				continue
+			}
+
+			o.buf.Refresh(nil)
+			switch r {
+			case CharEnter, CharCtrlJ:
+				o.history.Update(o.buf.Runes(), false)
+				fallthrough
+			case CharInterrupt:
+				o.t.KickRead()
+				fallthrough
+			case CharBell:
+				continue
+			}
+		}
+
+		if o.IsEnableVimMode() {
+			r = o.HandleVim(r, o.t.ReadRune)
+			if r == 0 {
+				continue
+			}
+		}
+
+		switch r {
+		case CharBell:
+			if o.IsSearchMode() {
+				o.ExitSearchMode(true)
+				o.buf.Refresh(nil)
+			}
+			if o.IsInCompleteMode() {
+				o.ExitCompleteMode(true)
+				o.buf.Refresh(nil)
+			}
+		case CharTab:
+			if o.cfg.AutoComplete == nil {
+				o.t.Bell()
+				break
+			}
+			o.OnComplete()
+			keepInCompleteMode = true
+		case CharBckSearch:
+			o.SearchMode(S_DIR_BCK)
+			keepInSearchMode = true
+		case CharCtrlU:
+			o.buf.KillFront()
+		case CharFwdSearch:
+			o.SearchMode(S_DIR_FWD)
+			keepInSearchMode = true
+		case CharKill:
+			o.buf.Kill()
+			keepInCompleteMode = true
+		case MetaForward:
+			o.buf.MoveToNextWord()
+		case CharTranspose:
+			o.buf.Transpose()
+		case MetaBackward:
+			o.buf.MoveToPrevWord()
+		case MetaDelete:
+			o.buf.DeleteWord()
+		case CharLineStart:
+			o.buf.MoveToLineStart()
+		case CharLineEnd:
+			o.buf.MoveToLineEnd()
+		case CharBackspace, CharCtrlH:
+			if o.IsSearchMode() {
+				o.SearchBackspace()
+				keepInSearchMode = true
+				break
+			}
+
+			if o.buf.Len() == 0 {
+				o.t.Bell()
+				break
+			}
+			o.buf.Backspace()
+			if o.IsInCompleteMode() {
+				o.OnComplete()
+			}
+		case CharCtrlZ:
+			o.buf.Clean()
+			o.t.SleepToResume()
+			o.Refresh()
+		case CharCtrlL:
+			ClearScreen(o.w)
+			o.Refresh()
+		case MetaBackspace, CharCtrlW:
+			o.buf.BackEscapeWord()
+		case CharEnter, CharCtrlJ:
+			if o.IsSearchMode() {
+				o.ExitSearchMode(false)
+			}
+			o.buf.MoveToLineEnd()
+			var data []rune
+			if !o.cfg.UniqueEditLine {
+				o.buf.WriteRune('\n')
+				data = o.buf.Reset()
+				data = data[:len(data)-1] // trim \n
+			} else {
+				o.buf.Clean()
+				data = o.buf.Reset()
+			}
+			o.outchan <- data
+			if !o.cfg.DisableAutoSaveHistory {
+				// ignore IO error
+				_ = o.history.New(data)
+			} else {
+				isUpdateHistory = false
+			}
+		case CharBackward:
+			o.buf.MoveBackward()
+		case CharForward:
+			o.buf.MoveForward()
+		case CharPrev:
+			buf := o.history.Prev()
+			if buf != nil {
+				o.buf.Set(buf)
+			} else {
+				o.t.Bell()
+			}
+		case CharNext:
+			buf, ok := o.history.Next()
+			if ok {
+				o.buf.Set(buf)
+			} else {
+				o.t.Bell()
+			}
+		case CharDelete:
+			if o.buf.Len() > 0 || !o.IsNormalMode() {
+				o.t.KickRead()
+				if !o.buf.Delete() {
+					o.t.Bell()
+				}
+				break
+			}
+
+			// treat as EOF
+			if !o.cfg.UniqueEditLine {
+				o.buf.WriteString(o.cfg.EOFPrompt + "\n")
+			}
+			o.buf.Reset()
+			isUpdateHistory = false
+			o.history.Revert()
+			o.errchan <- io.EOF
+			if o.cfg.UniqueEditLine {
+				o.buf.Clean()
+			}
+		case CharInterrupt:
+			if o.IsSearchMode() {
+				o.t.KickRead()
+				o.ExitSearchMode(true)
+				break
+			}
+			if o.IsInCompleteMode() {
+				o.t.KickRead()
+				o.ExitCompleteMode(true)
+				o.buf.Refresh(nil)
+				break
+			}
+			o.buf.MoveToLineEnd()
+			o.buf.Refresh(nil)
+			hint := o.cfg.InterruptPrompt + "\n"
+			if !o.cfg.UniqueEditLine {
+				o.buf.WriteString(hint)
+			}
+			remain := o.buf.Reset()
+			if !o.cfg.UniqueEditLine {
+				remain = remain[:len(remain)-len([]rune(hint))]
+			}
+			isUpdateHistory = false
+			o.history.Revert()
+			o.errchan <- &InterruptError{remain}
+		default:
+			if o.IsSearchMode() {
+				o.SearchChar(r)
+				keepInSearchMode = true
+				break
+			}
+			o.buf.WriteRune(r)
+			if o.IsInCompleteMode() {
+				o.OnComplete()
+				keepInCompleteMode = true
+			}
+		}
+
+		if o.cfg.Listener != nil {
+			newLine, newPos, ok := o.cfg.Listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
+			if ok {
+				o.buf.SetWithIdx(newPos, newLine)
+			}
+		}
+
+		if !keepInSearchMode && o.IsSearchMode() {
+			o.ExitSearchMode(false)
+			o.buf.Refresh(nil)
+		} else if o.IsInCompleteMode() {
+			if !keepInCompleteMode {
+				o.ExitCompleteMode(false)
+				o.Refresh()
+			} else {
+				o.buf.Refresh(nil)
+				o.CompleteRefresh()
+			}
+		}
+		if isUpdateHistory && !o.IsSearchMode() {
+			// it will cause null history
+			o.history.Update(o.buf.Runes(), false)
+		}
+	}
+}
+
+func (o *Operation) Stderr() io.Writer {
+	return &wrapWriter{target: o.cfg.Stderr, r: o, t: o.t}
+}
+
+func (o *Operation) Stdout() io.Writer {
+	return &wrapWriter{target: o.cfg.Stdout, r: o, t: o.t}
+}
+
+func (o *Operation) String() (string, error) {
+	r, err := o.Runes()
+	return string(r), err
+}
+
+func (o *Operation) Runes() ([]rune, error) {
+	o.t.EnterRawMode()
+	defer o.t.ExitRawMode()
+
+	if o.cfg.Listener != nil {
+		o.cfg.Listener.OnChange(nil, 0, 0)
+	}
+	o.buf.Refresh(nil) // print prompt
+	o.t.KickRead()
+	select {
+	case r := <-o.outchan:
+		return r, nil
+	case err := <-o.errchan:
+		if e, ok := err.(*InterruptError); ok {
+			return e.Line, ErrInterrupt
+		}
+		return nil, err
+	}
+}
+
+func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, error) {
+	cfg := o.GenPasswordConfig()
+	cfg.Prompt = prompt
+	cfg.Listener = l
+	return o.PasswordWithConfig(cfg)
+}
+
+func (o *Operation) GenPasswordConfig() *Config {
+	return o.opPassword.PasswordConfig()
+}
+
+func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
+	if err := o.opPassword.EnterPasswordMode(cfg); err != nil {
+		return nil, err
+	}
+	defer o.opPassword.ExitPasswordMode()
+	return o.Slice()
+}
+
+func (o *Operation) Password(prompt string) ([]byte, error) {
+	return o.PasswordEx(prompt, nil)
+}
+
+func (o *Operation) SetTitle(t string) {
+	o.w.Write([]byte("\033[2;" + t + "\007"))
+}
+
+func (o *Operation) Slice() ([]byte, error) {
+	r, err := o.Runes()
+	if err != nil {
+		return nil, err
+	}
+	return []byte(string(r)), nil
+}
+
+func (o *Operation) Close() {
+	o.history.Close()
+}
+
+func (o *Operation) SetHistoryPath(path string) {
+	if o.history != nil {
+		o.history.Close()
+	}
+	o.cfg.HistoryFile = path
+	o.history = newOpHistory(o.cfg)
+}
+
+func (o *Operation) IsNormalMode() bool {
+	return !o.IsInCompleteMode() && !o.IsSearchMode()
+}
+
+func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
+	if op.cfg == cfg {
+		return op.cfg, nil
+	}
+	if err := cfg.Init(); err != nil {
+		return op.cfg, err
+	}
+	old := op.cfg
+	op.cfg = cfg
+	op.SetPrompt(cfg.Prompt)
+	op.SetMaskRune(cfg.MaskRune)
+	op.buf.SetConfig(cfg)
+	width := op.cfg.FuncGetWidth()
+
+	if cfg.opHistory == nil {
+		op.SetHistoryPath(cfg.HistoryFile)
+		cfg.opHistory = op.history
+		cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.history, cfg, width)
+	}
+	op.history = cfg.opHistory
+
+	// SetHistoryPath will close opHistory which already exists
+	// so if we use it next time, we need to reopen it by `InitHistory()`
+	op.history.Init()
+
+	if op.cfg.AutoComplete != nil {
+		op.opCompleter = newOpCompleter(op.buf.w, op, width)
+	}
+
+	op.opSearch = cfg.opSearch
+	return old, nil
+}
+
+func (o *Operation) ResetHistory() {
+	o.history.Reset()
+}
+
+// if err is not nil, it just mean it fail to write to file
+// other things goes fine.
+func (o *Operation) SaveHistory(content string) error {
+	return o.history.New([]rune(content))
+}
+
+func (o *Operation) Refresh() {
+	if o.t.IsReading() {
+		o.buf.Refresh(nil)
+	}
+}
+
+func (o *Operation) Clean() {
+	o.buf.Clean()
+}
+
+func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
+	return &DumpListener{f: f}
+}
+
+type DumpListener struct {
+	f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
+}
+
+func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
+	return d.f(line, pos, key)
+}
+
+type Listener interface {
+	OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
+}
diff --git a/vendor/gopkg.in/readline.v1/password.go b/vendor/gopkg.in/readline.v1/password.go
new file mode 100644
index 0000000..4b07379
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/password.go
@@ -0,0 +1,32 @@
+package readline
+
+type opPassword struct {
+	o         *Operation
+	backupCfg *Config
+}
+
+func newOpPassword(o *Operation) *opPassword {
+	return &opPassword{o: o}
+}
+
+func (o *opPassword) ExitPasswordMode() {
+	o.o.SetConfig(o.backupCfg)
+	o.backupCfg = nil
+}
+
+func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) {
+	o.backupCfg, err = o.o.SetConfig(cfg)
+	return
+}
+
+func (o *opPassword) PasswordConfig() *Config {
+	return &Config{
+		EnableMask:      true,
+		InterruptPrompt: "\n",
+		EOFPrompt:       "\n",
+		HistoryLimit:    -1,
+
+		Stdout: o.o.cfg.Stdout,
+		Stderr: o.o.cfg.Stderr,
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/rawreader_windows.go b/vendor/gopkg.in/readline.v1/rawreader_windows.go
new file mode 100644
index 0000000..073ef15
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/rawreader_windows.go
@@ -0,0 +1,125 @@
+// +build windows
+
+package readline
+
+import "unsafe"
+
+const (
+	VK_CANCEL   = 0x03
+	VK_BACK     = 0x08
+	VK_TAB      = 0x09
+	VK_RETURN   = 0x0D
+	VK_SHIFT    = 0x10
+	VK_CONTROL  = 0x11
+	VK_MENU     = 0x12
+	VK_ESCAPE   = 0x1B
+	VK_LEFT     = 0x25
+	VK_UP       = 0x26
+	VK_RIGHT    = 0x27
+	VK_DOWN     = 0x28
+	VK_DELETE   = 0x2E
+	VK_LSHIFT   = 0xA0
+	VK_RSHIFT   = 0xA1
+	VK_LCONTROL = 0xA2
+	VK_RCONTROL = 0xA3
+)
+
+// RawReader translate input record to ANSI escape sequence.
+// To provides same behavior as unix terminal.
+type RawReader struct {
+	ctrlKey bool
+	altKey  bool
+}
+
+func NewRawReader() *RawReader {
+	r := new(RawReader)
+	return r
+}
+
+// only process one action in one read
+func (r *RawReader) Read(buf []byte) (int, error) {
+	ir := new(_INPUT_RECORD)
+	var read int
+	var err error
+next:
+	err = kernel.ReadConsoleInputW(stdin,
+		uintptr(unsafe.Pointer(ir)),
+		1,
+		uintptr(unsafe.Pointer(&read)),
+	)
+	if err != nil {
+		return 0, err
+	}
+	if ir.EventType != EVENT_KEY {
+		goto next
+	}
+	ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0]))
+	if ker.bKeyDown == 0 { // keyup
+		if r.ctrlKey || r.altKey {
+			switch ker.wVirtualKeyCode {
+			case VK_RCONTROL, VK_LCONTROL:
+				r.ctrlKey = false
+			case VK_MENU: //alt
+				r.altKey = false
+			}
+		}
+		goto next
+	}
+
+	if ker.unicodeChar == 0 {
+		var target rune
+		switch ker.wVirtualKeyCode {
+		case VK_RCONTROL, VK_LCONTROL:
+			r.ctrlKey = true
+		case VK_MENU: //alt
+			r.altKey = true
+		case VK_LEFT:
+			target = CharBackward
+		case VK_RIGHT:
+			target = CharForward
+		case VK_UP:
+			target = CharPrev
+		case VK_DOWN:
+			target = CharNext
+		}
+		if target != 0 {
+			return r.write(buf, target)
+		}
+		goto next
+	}
+	char := rune(ker.unicodeChar)
+	if r.ctrlKey {
+		switch char {
+		case 'A':
+			char = CharLineStart
+		case 'E':
+			char = CharLineEnd
+		case 'R':
+			char = CharBckSearch
+		case 'S':
+			char = CharFwdSearch
+		}
+	} else if r.altKey {
+		switch char {
+		case VK_BACK:
+			char = CharBackspace
+		}
+		return r.writeEsc(buf, char)
+	}
+	return r.write(buf, char)
+}
+
+func (r *RawReader) writeEsc(b []byte, char rune) (int, error) {
+	b[0] = '\033'
+	n := copy(b[1:], []byte(string(char)))
+	return n + 1, nil
+}
+
+func (r *RawReader) write(b []byte, char rune) (int, error) {
+	n := copy(b, []byte(string(char)))
+	return n, nil
+}
+
+func (r *RawReader) Close() error {
+	return nil
+}
diff --git a/vendor/gopkg.in/readline.v1/readline.go b/vendor/gopkg.in/readline.v1/readline.go
new file mode 100644
index 0000000..4b866fb
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/readline.go
@@ -0,0 +1,257 @@
+package readline
+
+import "io"
+
+type Instance struct {
+	Config    *Config
+	Terminal  *Terminal
+	Operation *Operation
+}
+
+type Config struct {
+	// prompt supports ANSI escape sequence, so we can color some characters even in windows
+	Prompt string
+
+	// readline will persist historys to file where HistoryFile specified
+	HistoryFile string
+	// specify the max length of historys, it's 500 by default, set it to -1 to disable history
+	HistoryLimit           int
+	DisableAutoSaveHistory bool
+
+	// AutoCompleter will called once user press TAB
+	AutoComplete AutoCompleter
+
+	// Any key press will pass to Listener
+	// NOTE: Listener will be triggered by (nil, 0, 0) immediately
+	Listener Listener
+
+	// If VimMode is true, readline will in vim.insert mode by default
+	VimMode bool
+
+	InterruptPrompt string
+	EOFPrompt       string
+
+	FuncGetWidth func() int
+
+	Stdin  io.Reader
+	Stdout io.Writer
+	Stderr io.Writer
+
+	EnableMask bool
+	MaskRune   rune
+
+	// erase the editing line after user submited it
+	// it use in IM usually.
+	UniqueEditLine bool
+
+	// force use interactive even stdout is not a tty
+	FuncIsTerminal      func() bool
+	FuncMakeRaw         func() error
+	FuncExitRaw         func() error
+	FuncOnWidthChanged  func(func())
+	ForceUseInteractive bool
+
+	// private fields
+	inited    bool
+	opHistory *opHistory
+	opSearch  *opSearch
+}
+
+func (c *Config) useInteractive() bool {
+	if c.ForceUseInteractive {
+		return true
+	}
+	return c.FuncIsTerminal()
+}
+
+func (c *Config) Init() error {
+	if c.inited {
+		return nil
+	}
+	c.inited = true
+	if c.Stdin == nil {
+		c.Stdin = Stdin
+	}
+	if c.Stdout == nil {
+		c.Stdout = Stdout
+	}
+	if c.Stderr == nil {
+		c.Stderr = Stderr
+	}
+	if c.HistoryLimit == 0 {
+		c.HistoryLimit = 500
+	}
+
+	if c.InterruptPrompt == "" {
+		c.InterruptPrompt = "^C"
+	} else if c.InterruptPrompt == "\n" {
+		c.InterruptPrompt = ""
+	}
+	if c.EOFPrompt == "" {
+		c.EOFPrompt = "^D"
+	} else if c.EOFPrompt == "\n" {
+		c.EOFPrompt = ""
+	}
+
+	if c.FuncGetWidth == nil {
+		c.FuncGetWidth = GetScreenWidth
+	}
+	if c.FuncIsTerminal == nil {
+		c.FuncIsTerminal = DefaultIsTerminal
+	}
+	rm := new(RawMode)
+	if c.FuncMakeRaw == nil {
+		c.FuncMakeRaw = rm.Enter
+	}
+	if c.FuncExitRaw == nil {
+		c.FuncExitRaw = rm.Exit
+	}
+	if c.FuncOnWidthChanged == nil {
+		c.FuncOnWidthChanged = DefaultOnWidthChanged
+	}
+
+	return nil
+}
+
+func (c Config) Clone() *Config {
+	c.opHistory = nil
+	c.opSearch = nil
+	return &c
+}
+
+func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {
+	c.Listener = FuncListener(f)
+}
+
+func NewEx(cfg *Config) (*Instance, error) {
+	t, err := NewTerminal(cfg)
+	if err != nil {
+		return nil, err
+	}
+	rl := t.Readline()
+	return &Instance{
+		Config:    cfg,
+		Terminal:  t,
+		Operation: rl,
+	}, nil
+}
+
+func New(prompt string) (*Instance, error) {
+	return NewEx(&Config{Prompt: prompt})
+}
+
+func (i *Instance) ResetHistory() {
+	i.Operation.ResetHistory()
+}
+
+func (i *Instance) SetPrompt(s string) {
+	i.Operation.SetPrompt(s)
+}
+
+func (i *Instance) SetMaskRune(r rune) {
+	i.Operation.SetMaskRune(r)
+}
+
+// change history persistence in runtime
+func (i *Instance) SetHistoryPath(p string) {
+	i.Operation.SetHistoryPath(p)
+}
+
+// readline will refresh automatic when write through Stdout()
+func (i *Instance) Stdout() io.Writer {
+	return i.Operation.Stdout()
+}
+
+// readline will refresh automatic when write through Stdout()
+func (i *Instance) Stderr() io.Writer {
+	return i.Operation.Stderr()
+}
+
+// switch VimMode in runtime
+func (i *Instance) SetVimMode(on bool) {
+	i.Operation.SetVimMode(on)
+}
+
+func (i *Instance) IsVimMode() bool {
+	return i.Operation.IsEnableVimMode()
+}
+
+func (i *Instance) GenPasswordConfig() *Config {
+	return i.Operation.GenPasswordConfig()
+}
+
+// we can generate a config by `i.GenPasswordConfig()`
+func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
+	return i.Operation.PasswordWithConfig(cfg)
+}
+
+func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {
+	return i.Operation.PasswordEx(prompt, l)
+}
+
+func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
+	return i.Operation.Password(prompt)
+}
+
+type Result struct {
+	Line  string
+	Error error
+}
+
+func (l *Result) CanContinue() bool {
+	return len(l.Line) != 0 && l.Error == ErrInterrupt
+}
+
+func (l *Result) CanBreak() bool {
+	return !l.CanContinue() && l.Error != nil
+}
+
+func (i *Instance) Line() *Result {
+	ret, err := i.Readline()
+	return &Result{ret, err}
+}
+
+// err is one of (nil, io.EOF, readline.ErrInterrupt)
+func (i *Instance) Readline() (string, error) {
+	return i.Operation.String()
+}
+
+func (i *Instance) SaveHistory(content string) error {
+	return i.Operation.SaveHistory(content)
+}
+
+// same as readline
+func (i *Instance) ReadSlice() ([]byte, error) {
+	return i.Operation.Slice()
+}
+
+// we must make sure that call Close() before process exit.
+func (i *Instance) Close() error {
+	if err := i.Terminal.Close(); err != nil {
+		return err
+	}
+	i.Operation.Close()
+	return nil
+}
+func (i *Instance) Clean() {
+	i.Operation.Clean()
+}
+
+func (i *Instance) Write(b []byte) (int, error) {
+	return i.Stdout().Write(b)
+}
+
+func (i *Instance) SetConfig(cfg *Config) *Config {
+	if i.Config == cfg {
+		return cfg
+	}
+	old := i.Config
+	i.Config = cfg
+	i.Operation.SetConfig(cfg)
+	i.Terminal.SetConfig(cfg)
+	return old
+}
+
+func (i *Instance) Refresh() {
+	i.Operation.Refresh()
+}
diff --git a/vendor/gopkg.in/readline.v1/remote.go b/vendor/gopkg.in/readline.v1/remote.go
new file mode 100644
index 0000000..db77ae8
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/remote.go
@@ -0,0 +1,474 @@
+package readline
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"net"
+	"os"
+	"sync"
+	"sync/atomic"
+)
+
+type MsgType int16
+
+const (
+	T_DATA = MsgType(iota)
+	T_WIDTH
+	T_WIDTH_REPORT
+	T_ISTTY_REPORT
+	T_RAW
+	T_ERAW // exit raw
+	T_EOF
+)
+
+type RemoteSvr struct {
+	eof           int32
+	closed        int32
+	width         int32
+	reciveChan    chan struct{}
+	writeChan     chan *writeCtx
+	conn          net.Conn
+	isTerminal    bool
+	funcWidthChan func()
+	stopChan      chan struct{}
+
+	dataBufM sync.Mutex
+	dataBuf  bytes.Buffer
+}
+
+type writeReply struct {
+	n   int
+	err error
+}
+
+type writeCtx struct {
+	msg   *Message
+	reply chan *writeReply
+}
+
+func newWriteCtx(msg *Message) *writeCtx {
+	return &writeCtx{
+		msg:   msg,
+		reply: make(chan *writeReply),
+	}
+}
+
+func NewRemoteSvr(conn net.Conn) (*RemoteSvr, error) {
+	rs := &RemoteSvr{
+		width:      -1,
+		conn:       conn,
+		writeChan:  make(chan *writeCtx),
+		reciveChan: make(chan struct{}),
+		stopChan:   make(chan struct{}),
+	}
+	buf := bufio.NewReader(rs.conn)
+
+	if err := rs.init(buf); err != nil {
+		return nil, err
+	}
+
+	go rs.readLoop(buf)
+	go rs.writeLoop()
+	return rs, nil
+}
+
+func (r *RemoteSvr) init(buf *bufio.Reader) error {
+	m, err := ReadMessage(buf)
+	if err != nil {
+		return err
+	}
+	// receive isTerminal
+	if m.Type != T_ISTTY_REPORT {
+		return fmt.Errorf("unexpected init message")
+	}
+	r.GotIsTerminal(m.Data)
+
+	// receive width
+	m, err = ReadMessage(buf)
+	if err != nil {
+		return err
+	}
+	if m.Type != T_WIDTH_REPORT {
+		return fmt.Errorf("unexpected init message")
+	}
+	r.GotReportWidth(m.Data)
+
+	return nil
+}
+
+func (r *RemoteSvr) HandleConfig(cfg *Config) {
+	cfg.Stderr = r
+	cfg.Stdout = r
+	cfg.Stdin = r
+	cfg.FuncExitRaw = r.ExitRawMode
+	cfg.FuncIsTerminal = r.IsTerminal
+	cfg.FuncMakeRaw = r.EnterRawMode
+	cfg.FuncExitRaw = r.ExitRawMode
+	cfg.FuncGetWidth = r.GetWidth
+	cfg.FuncOnWidthChanged = func(f func()) {
+		r.funcWidthChan = f
+	}
+}
+
+func (r *RemoteSvr) IsTerminal() bool {
+	return r.isTerminal
+}
+
+func (r *RemoteSvr) checkEOF() error {
+	if atomic.LoadInt32(&r.eof) == 1 {
+		return io.EOF
+	}
+	return nil
+}
+
+func (r *RemoteSvr) Read(b []byte) (int, error) {
+	r.dataBufM.Lock()
+	n, err := r.dataBuf.Read(b)
+	r.dataBufM.Unlock()
+	if n == 0 {
+		if err := r.checkEOF(); err != nil {
+			return 0, err
+		}
+	}
+
+	if n == 0 && err == io.EOF {
+		<-r.reciveChan
+		r.dataBufM.Lock()
+		n, err = r.dataBuf.Read(b)
+		r.dataBufM.Unlock()
+	}
+	if n == 0 {
+		if err := r.checkEOF(); err != nil {
+			return 0, err
+		}
+	}
+
+	return n, err
+}
+
+func (r *RemoteSvr) writeMsg(m *Message) error {
+	ctx := newWriteCtx(m)
+	r.writeChan <- ctx
+	reply := <-ctx.reply
+	return reply.err
+}
+
+func (r *RemoteSvr) Write(b []byte) (int, error) {
+	ctx := newWriteCtx(NewMessage(T_DATA, b))
+	r.writeChan <- ctx
+	reply := <-ctx.reply
+	return reply.n, reply.err
+}
+
+func (r *RemoteSvr) EnterRawMode() error {
+	return r.writeMsg(NewMessage(T_RAW, nil))
+}
+
+func (r *RemoteSvr) ExitRawMode() error {
+	return r.writeMsg(NewMessage(T_ERAW, nil))
+}
+
+func (r *RemoteSvr) writeLoop() {
+	defer r.Close()
+
+loop:
+	for {
+		select {
+		case ctx, ok := <-r.writeChan:
+			if !ok {
+				break
+			}
+			n, err := ctx.msg.WriteTo(r.conn)
+			ctx.reply <- &writeReply{n, err}
+		case <-r.stopChan:
+			break loop
+		}
+	}
+}
+
+func (r *RemoteSvr) Close() {
+	if atomic.CompareAndSwapInt32(&r.closed, 0, 1) {
+		close(r.stopChan)
+		r.conn.Close()
+	}
+}
+
+func (r *RemoteSvr) readLoop(buf *bufio.Reader) {
+	defer r.Close()
+	for {
+		m, err := ReadMessage(buf)
+		if err != nil {
+			break
+		}
+		switch m.Type {
+		case T_EOF:
+			atomic.StoreInt32(&r.eof, 1)
+			select {
+			case r.reciveChan <- struct{}{}:
+			default:
+			}
+		case T_DATA:
+			r.dataBufM.Lock()
+			r.dataBuf.Write(m.Data)
+			r.dataBufM.Unlock()
+			select {
+			case r.reciveChan <- struct{}{}:
+			default:
+			}
+		case T_WIDTH_REPORT:
+			r.GotReportWidth(m.Data)
+		case T_ISTTY_REPORT:
+			r.GotIsTerminal(m.Data)
+		}
+	}
+}
+
+func (r *RemoteSvr) GotIsTerminal(data []byte) {
+	if binary.BigEndian.Uint16(data) == 0 {
+		r.isTerminal = false
+	} else {
+		r.isTerminal = true
+	}
+}
+
+func (r *RemoteSvr) GotReportWidth(data []byte) {
+	atomic.StoreInt32(&r.width, int32(binary.BigEndian.Uint16(data)))
+	if r.funcWidthChan != nil {
+		r.funcWidthChan()
+	}
+}
+
+func (r *RemoteSvr) GetWidth() int {
+	return int(atomic.LoadInt32(&r.width))
+}
+
+// -----------------------------------------------------------------------------
+
+type Message struct {
+	Type MsgType
+	Data []byte
+}
+
+func ReadMessage(r io.Reader) (*Message, error) {
+	m := new(Message)
+	var length int32
+	if err := binary.Read(r, binary.BigEndian, &length); err != nil {
+		return nil, err
+	}
+	if err := binary.Read(r, binary.BigEndian, &m.Type); err != nil {
+		return nil, err
+	}
+	m.Data = make([]byte, int(length)-2)
+	if _, err := io.ReadFull(r, m.Data); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func NewMessage(t MsgType, data []byte) *Message {
+	return &Message{t, data}
+}
+
+func (m *Message) WriteTo(w io.Writer) (int, error) {
+	buf := bytes.NewBuffer(make([]byte, 0, len(m.Data)+2+4))
+	binary.Write(buf, binary.BigEndian, int32(len(m.Data)+2))
+	binary.Write(buf, binary.BigEndian, m.Type)
+	buf.Write(m.Data)
+	n, err := buf.WriteTo(w)
+	return int(n), err
+}
+
+// -----------------------------------------------------------------------------
+
+type RemoteCli struct {
+	conn        net.Conn
+	raw         RawMode
+	receiveChan chan struct{}
+	inited      int32
+	isTerminal  *bool
+
+	data  bytes.Buffer
+	dataM sync.Mutex
+}
+
+func NewRemoteCli(conn net.Conn) (*RemoteCli, error) {
+	r := &RemoteCli{
+		conn:        conn,
+		receiveChan: make(chan struct{}),
+	}
+	return r, nil
+}
+
+func (r *RemoteCli) MarkIsTerminal(is bool) {
+	r.isTerminal = &is
+}
+
+func (r *RemoteCli) init() error {
+	if !atomic.CompareAndSwapInt32(&r.inited, 0, 1) {
+		return nil
+	}
+
+	if err := r.reportIsTerminal(); err != nil {
+		return err
+	}
+
+	if err := r.reportWidth(); err != nil {
+		return err
+	}
+
+	// register sig for width changed
+	DefaultOnWidthChanged(func() {
+		r.reportWidth()
+	})
+	return nil
+}
+
+func (r *RemoteCli) writeMsg(m *Message) error {
+	r.dataM.Lock()
+	_, err := m.WriteTo(r.conn)
+	r.dataM.Unlock()
+	return err
+}
+
+func (r *RemoteCli) Write(b []byte) (int, error) {
+	m := NewMessage(T_DATA, b)
+	r.dataM.Lock()
+	_, err := m.WriteTo(r.conn)
+	r.dataM.Unlock()
+	return len(b), err
+}
+
+func (r *RemoteCli) reportWidth() error {
+	screenWidth := GetScreenWidth()
+	data := make([]byte, 2)
+	binary.BigEndian.PutUint16(data, uint16(screenWidth))
+	msg := NewMessage(T_WIDTH_REPORT, data)
+
+	if err := r.writeMsg(msg); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (r *RemoteCli) reportIsTerminal() error {
+	var isTerminal bool
+	if r.isTerminal != nil {
+		isTerminal = *r.isTerminal
+	} else {
+		isTerminal = DefaultIsTerminal()
+	}
+	data := make([]byte, 2)
+	if isTerminal {
+		binary.BigEndian.PutUint16(data, 1)
+	} else {
+		binary.BigEndian.PutUint16(data, 0)
+	}
+	msg := NewMessage(T_ISTTY_REPORT, data)
+	if err := r.writeMsg(msg); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (r *RemoteCli) readLoop() {
+	buf := bufio.NewReader(r.conn)
+	for {
+		msg, err := ReadMessage(buf)
+		if err != nil {
+			break
+		}
+		switch msg.Type {
+		case T_ERAW:
+			r.raw.Exit()
+		case T_RAW:
+			r.raw.Enter()
+		case T_DATA:
+			os.Stdout.Write(msg.Data)
+		}
+	}
+}
+
+func (r *RemoteCli) ServeBy(source io.Reader) error {
+	if err := r.init(); err != nil {
+		return err
+	}
+
+	go func() {
+		defer r.Close()
+		for {
+			n, _ := io.Copy(r, source)
+			if n == 0 {
+				break
+			}
+		}
+	}()
+	defer r.raw.Exit()
+	r.readLoop()
+	return nil
+}
+
+func (r *RemoteCli) Close() {
+	r.writeMsg(NewMessage(T_EOF, nil))
+}
+
+func (r *RemoteCli) Serve() error {
+	return r.ServeBy(os.Stdin)
+}
+
+func ListenRemote(n, addr string, cfg *Config, h func(*Instance), onListen ...func(net.Listener) error) error {
+	ln, err := net.Listen(n, addr)
+	if err != nil {
+		return err
+	}
+	if len(onListen) > 0 {
+		if err := onListen[0](ln); err != nil {
+			return err
+		}
+	}
+	for {
+		conn, err := ln.Accept()
+		if err != nil {
+			break
+		}
+		go func() {
+			defer conn.Close()
+			rl, err := HandleConn(*cfg, conn)
+			if err != nil {
+				return
+			}
+			h(rl)
+		}()
+	}
+	return nil
+}
+
+func HandleConn(cfg Config, conn net.Conn) (*Instance, error) {
+	r, err := NewRemoteSvr(conn)
+	if err != nil {
+		return nil, err
+	}
+	r.HandleConfig(&cfg)
+
+	rl, err := NewEx(&cfg)
+	if err != nil {
+		return nil, err
+	}
+	return rl, nil
+}
+
+func DialRemote(n, addr string) error {
+	conn, err := net.Dial(n, addr)
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	cli, err := NewRemoteCli(conn)
+	if err != nil {
+		return err
+	}
+	return cli.Serve()
+}
diff --git a/vendor/gopkg.in/readline.v1/runebuf.go b/vendor/gopkg.in/readline.v1/runebuf.go
new file mode 100644
index 0000000..3555b04
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/runebuf.go
@@ -0,0 +1,500 @@
+package readline
+
+import (
+	"bufio"
+	"bytes"
+	"io"
+	"strings"
+)
+
+type runeBufferBck struct {
+	buf []rune
+	idx int
+}
+
+type RuneBuffer struct {
+	buf    []rune
+	idx    int
+	prompt []rune
+	w      io.Writer
+
+	hadClean    bool
+	interactive bool
+	cfg         *Config
+
+	width int
+
+	bck *runeBufferBck
+}
+
+func (r *RuneBuffer) OnWidthChange(newWidth int) {
+	r.width = newWidth
+}
+
+func (r *RuneBuffer) Backup() {
+	r.bck = &runeBufferBck{r.buf, r.idx}
+}
+
+func (r *RuneBuffer) Restore() {
+	r.Refresh(func() {
+		if r.bck == nil {
+			return
+		}
+		r.buf = r.bck.buf
+		r.idx = r.bck.idx
+	})
+}
+
+func NewRuneBuffer(w io.Writer, prompt string, cfg *Config, width int) *RuneBuffer {
+	rb := &RuneBuffer{
+		w:           w,
+		interactive: cfg.useInteractive(),
+		cfg:         cfg,
+		width:       width,
+	}
+	rb.SetPrompt(prompt)
+	return rb
+}
+
+func (r *RuneBuffer) SetConfig(cfg *Config) {
+	r.cfg = cfg
+	r.interactive = cfg.useInteractive()
+}
+
+func (r *RuneBuffer) SetMask(m rune) {
+	r.cfg.MaskRune = m
+}
+
+func (r *RuneBuffer) CurrentWidth(x int) int {
+	return runes.WidthAll(r.buf[:x])
+}
+
+func (r *RuneBuffer) PromptLen() int {
+	return runes.WidthAll(runes.ColorFilter(r.prompt))
+}
+
+func (r *RuneBuffer) RuneSlice(i int) []rune {
+	if i > 0 {
+		rs := make([]rune, i)
+		copy(rs, r.buf[r.idx:r.idx+i])
+		return rs
+	}
+	rs := make([]rune, -i)
+	copy(rs, r.buf[r.idx+i:r.idx])
+	return rs
+}
+
+func (r *RuneBuffer) Runes() []rune {
+	newr := make([]rune, len(r.buf))
+	copy(newr, r.buf)
+	return newr
+}
+
+func (r *RuneBuffer) Pos() int {
+	return r.idx
+}
+
+func (r *RuneBuffer) Len() int {
+	return len(r.buf)
+}
+
+func (r *RuneBuffer) MoveToLineStart() {
+	r.Refresh(func() {
+		if r.idx == 0 {
+			return
+		}
+		r.idx = 0
+	})
+}
+
+func (r *RuneBuffer) MoveBackward() {
+	r.Refresh(func() {
+		if r.idx == 0 {
+			return
+		}
+		r.idx--
+	})
+}
+
+func (r *RuneBuffer) WriteString(s string) {
+	r.WriteRunes([]rune(s))
+}
+
+func (r *RuneBuffer) WriteRune(s rune) {
+	r.WriteRunes([]rune{s})
+}
+
+func (r *RuneBuffer) WriteRunes(s []rune) {
+	r.Refresh(func() {
+		tail := append(s, r.buf[r.idx:]...)
+		r.buf = append(r.buf[:r.idx], tail...)
+		r.idx += len(s)
+	})
+}
+
+func (r *RuneBuffer) MoveForward() {
+	r.Refresh(func() {
+		if r.idx == len(r.buf) {
+			return
+		}
+		r.idx++
+	})
+}
+
+func (r *RuneBuffer) IsCursorInEnd() bool {
+	return r.idx == len(r.buf)
+}
+
+func (r *RuneBuffer) Replace(ch rune) {
+	r.Refresh(func() {
+		r.buf[r.idx] = ch
+	})
+}
+
+func (r *RuneBuffer) Erase() {
+	r.Refresh(func() {
+		r.idx = 0
+		r.buf = r.buf[:0]
+	})
+}
+
+func (r *RuneBuffer) Delete() (success bool) {
+	r.Refresh(func() {
+		if r.idx == len(r.buf) {
+			return
+		}
+		r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...)
+		success = true
+	})
+	return
+}
+
+func (r *RuneBuffer) DeleteWord() {
+	if r.idx == len(r.buf) {
+		return
+	}
+	init := r.idx
+	for init < len(r.buf) && IsWordBreak(r.buf[init]) {
+		init++
+	}
+	for i := init + 1; i < len(r.buf); i++ {
+		if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+			r.Refresh(func() {
+				r.buf = append(r.buf[:r.idx], r.buf[i-1:]...)
+			})
+			return
+		}
+	}
+	r.Kill()
+}
+
+func (r *RuneBuffer) MoveToPrevWord() (success bool) {
+	r.Refresh(func() {
+		if r.idx == 0 {
+			return
+		}
+
+		for i := r.idx - 1; i > 0; i-- {
+			if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+				r.idx = i
+				success = true
+				return
+			}
+		}
+		r.idx = 0
+		success = true
+	})
+	return
+}
+
+func (r *RuneBuffer) KillFront() {
+	r.Refresh(func() {
+		if r.idx == 0 {
+			return
+		}
+
+		length := len(r.buf) - r.idx
+		copy(r.buf[:length], r.buf[r.idx:])
+		r.idx = 0
+		r.buf = r.buf[:length]
+	})
+}
+
+func (r *RuneBuffer) Kill() {
+	r.Refresh(func() {
+		r.buf = r.buf[:r.idx]
+	})
+}
+
+func (r *RuneBuffer) Transpose() {
+	r.Refresh(func() {
+		if len(r.buf) == 1 {
+			r.idx++
+		}
+
+		if len(r.buf) < 2 {
+			return
+		}
+
+		if r.idx == 0 {
+			r.idx = 1
+		} else if r.idx >= len(r.buf) {
+			r.idx = len(r.buf) - 1
+		}
+		r.buf[r.idx], r.buf[r.idx-1] = r.buf[r.idx-1], r.buf[r.idx]
+		r.idx++
+	})
+}
+
+func (r *RuneBuffer) MoveToNextWord() {
+	r.Refresh(func() {
+		for i := r.idx + 1; i < len(r.buf); i++ {
+			if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+				r.idx = i
+				return
+			}
+		}
+
+		r.idx = len(r.buf)
+	})
+}
+
+func (r *RuneBuffer) MoveToEndWord() {
+	r.Refresh(func() {
+		// already at the end, so do nothing
+		if r.idx == len(r.buf) {
+			return
+		}
+		// if we are at the end of a word already, go to next
+		if !IsWordBreak(r.buf[r.idx]) && IsWordBreak(r.buf[r.idx+1]) {
+			r.idx++
+		}
+
+		// keep going until at the end of a word
+		for i := r.idx + 1; i < len(r.buf); i++ {
+			if IsWordBreak(r.buf[i]) && !IsWordBreak(r.buf[i-1]) {
+				r.idx = i - 1
+				return
+			}
+		}
+		r.idx = len(r.buf)
+	})
+}
+
+func (r *RuneBuffer) BackEscapeWord() {
+	r.Refresh(func() {
+		if r.idx == 0 {
+			return
+		}
+		for i := r.idx - 1; i > 0; i-- {
+			if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+				r.buf = append(r.buf[:i], r.buf[r.idx:]...)
+				r.idx = i
+				return
+			}
+		}
+
+		r.buf = r.buf[:0]
+		r.idx = 0
+	})
+}
+
+func (r *RuneBuffer) Backspace() {
+	r.Refresh(func() {
+		if r.idx == 0 {
+			return
+		}
+
+		r.idx--
+		r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...)
+	})
+}
+
+func (r *RuneBuffer) MoveToLineEnd() {
+	r.Refresh(func() {
+		if r.idx == len(r.buf) {
+			return
+		}
+
+		r.idx = len(r.buf)
+	})
+}
+
+func (r *RuneBuffer) LineCount(width int) int {
+	if width == -1 {
+		width = r.width
+	}
+	return LineCount(width,
+		runes.WidthAll(r.buf)+r.PromptLen())
+}
+
+func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
+	r.Refresh(func() {
+		if reverse {
+			for i := r.idx - 1; i >= 0; i-- {
+				if r.buf[i] == ch {
+					r.idx = i
+					if prevChar {
+						r.idx++
+					}
+					success = true
+					return
+				}
+			}
+			return
+		}
+		for i := r.idx + 1; i < len(r.buf); i++ {
+			if r.buf[i] == ch {
+				r.idx = i
+				if prevChar {
+					r.idx--
+				}
+				success = true
+				return
+			}
+		}
+	})
+	return
+}
+
+func (r *RuneBuffer) isInLineEdge() bool {
+	if isWindows {
+		return false
+	}
+	sp := r.getSplitByLine(r.buf)
+	return len(sp[len(sp)-1]) == 0
+}
+
+func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
+	return SplitByLine(r.PromptLen(), r.width, rs)
+}
+
+func (r *RuneBuffer) IdxLine(width int) int {
+	sp := r.getSplitByLine(r.buf[:r.idx])
+	return len(sp) - 1
+}
+
+func (r *RuneBuffer) CursorLineCount() int {
+	return r.LineCount(r.width) - r.IdxLine(r.width)
+}
+
+func (r *RuneBuffer) Refresh(f func()) {
+	if !r.interactive {
+		if f != nil {
+			f()
+		}
+		return
+	}
+	r.Clean()
+	if f != nil {
+		f()
+	}
+	r.print()
+}
+
+func (r *RuneBuffer) print() {
+	r.w.Write(r.output())
+	r.hadClean = false
+}
+
+func (r *RuneBuffer) output() []byte {
+	buf := bytes.NewBuffer(nil)
+	buf.WriteString(string(r.prompt))
+	if r.cfg.EnableMask && len(r.buf) > 0 {
+		buf.Write([]byte(strings.Repeat(string(r.cfg.MaskRune), len(r.buf)-1)))
+		if r.buf[len(r.buf)-1] == '\n' {
+			buf.Write([]byte{'\n'})
+		} else {
+			buf.Write([]byte(string(r.cfg.MaskRune)))
+		}
+		if len(r.buf) > r.idx {
+			buf.Write(runes.Backspace(r.buf[r.idx:]))
+		}
+
+	} else {
+		buf.Write([]byte(string(r.buf)))
+		if r.isInLineEdge() {
+			buf.Write([]byte(" \b"))
+		}
+	}
+
+	if len(r.buf) > r.idx {
+		buf.Write(runes.Backspace(r.buf[r.idx:]))
+	}
+	return buf.Bytes()
+}
+
+func (r *RuneBuffer) Reset() []rune {
+	ret := runes.Copy(r.buf)
+	r.buf = r.buf[:0]
+	r.idx = 0
+	return ret
+}
+
+func (r *RuneBuffer) calWidth(m int) int {
+	if m > 0 {
+		return runes.WidthAll(r.buf[r.idx : r.idx+m])
+	}
+	return runes.WidthAll(r.buf[r.idx+m : r.idx])
+}
+
+func (r *RuneBuffer) SetStyle(start, end int, style string) {
+	if end < start {
+		panic("end < start")
+	}
+
+	// goto start
+	move := start - r.idx
+	if move > 0 {
+		r.w.Write([]byte(string(r.buf[r.idx : r.idx+move])))
+	} else {
+		r.w.Write(bytes.Repeat([]byte("\b"), r.calWidth(move)))
+	}
+	r.w.Write([]byte("\033[" + style + "m"))
+	r.w.Write([]byte(string(r.buf[start:end])))
+	r.w.Write([]byte("\033[0m"))
+	// TODO: move back
+}
+
+func (r *RuneBuffer) SetWithIdx(idx int, buf []rune) {
+	r.Refresh(func() {
+		r.buf = buf
+		r.idx = idx
+	})
+}
+
+func (r *RuneBuffer) Set(buf []rune) {
+	r.SetWithIdx(len(buf), buf)
+}
+
+func (r *RuneBuffer) SetPrompt(prompt string) {
+	r.prompt = []rune(prompt)
+}
+
+func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
+	buf := bufio.NewWriter(w)
+	buf.Write([]byte("\033[J")) // just like ^k :)
+
+	if idxLine == 0 {
+		io.WriteString(buf, "\033[2K\r")
+	} else {
+		for i := 0; i < idxLine; i++ {
+			io.WriteString(buf, "\033[2K\r\033[A")
+		}
+		io.WriteString(buf, "\033[2K\r")
+	}
+	buf.Flush()
+	return
+}
+
+func (r *RuneBuffer) Clean() {
+	r.clean(r.IdxLine(r.width))
+}
+
+func (r *RuneBuffer) clean(idxLine int) {
+	if r.hadClean || !r.interactive {
+		return
+	}
+	r.hadClean = true
+	r.cleanOutput(r.w, idxLine)
+}
diff --git a/vendor/gopkg.in/readline.v1/runes.go b/vendor/gopkg.in/readline.v1/runes.go
new file mode 100644
index 0000000..b85af7a
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/runes.go
@@ -0,0 +1,167 @@
+package readline
+
+import (
+	"bytes"
+	"unicode"
+)
+
+var runes = Runes{}
+
+type Runes struct{}
+
+func (Runes) Equal(a, b []rune) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i := 0; i < len(a); i++ {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+	return true
+}
+
+// Search in runes from end to front
+func (Runes) IndexAllBck(r, sub []rune) int {
+	for i := len(r) - len(sub); i >= 0; i-- {
+		found := true
+		for j := 0; j < len(sub); j++ {
+			if r[i+j] != sub[j] {
+				found = false
+				break
+			}
+		}
+		if found {
+			return i
+		}
+	}
+	return -1
+}
+
+// Search in runes from front to end
+func (Runes) IndexAll(r, sub []rune) int {
+	for i := 0; i < len(r); i++ {
+		found := true
+		if len(r[i:]) < len(sub) {
+			return -1
+		}
+		for j := 0; j < len(sub); j++ {
+			if r[i+j] != sub[j] {
+				found = false
+				break
+			}
+		}
+		if found {
+			return i
+		}
+	}
+	return -1
+}
+
+func (Runes) Index(r rune, rs []rune) int {
+	for i := 0; i < len(rs); i++ {
+		if rs[i] == r {
+			return i
+		}
+	}
+	return -1
+}
+
+func (Runes) ColorFilter(r []rune) []rune {
+	newr := make([]rune, 0, len(r))
+	for pos := 0; pos < len(r); pos++ {
+		if r[pos] == '\033' && r[pos+1] == '[' {
+			idx := runes.Index('m', r[pos+2:])
+			if idx == -1 {
+				continue
+			}
+			pos += idx + 2
+			continue
+		}
+		newr = append(newr, r[pos])
+	}
+	return newr
+}
+
+var zeroWidth = []*unicode.RangeTable{
+	unicode.Mn,
+	unicode.Me,
+	unicode.Cc,
+	unicode.Cf,
+}
+
+var doubleWidth = []*unicode.RangeTable{
+	unicode.Han,
+	unicode.Hangul,
+	unicode.Hiragana,
+	unicode.Katakana,
+}
+
+func (Runes) Width(r rune) int {
+	if unicode.IsOneOf(zeroWidth, r) {
+		return 0
+	}
+	if unicode.IsOneOf(doubleWidth, r) {
+		return 2
+	}
+	return 1
+}
+
+func (Runes) WidthAll(r []rune) (length int) {
+	for i := 0; i < len(r); i++ {
+		length += runes.Width(r[i])
+	}
+	return
+}
+
+func (Runes) Backspace(r []rune) []byte {
+	return bytes.Repeat([]byte{'\b'}, runes.WidthAll(r))
+}
+
+func (Runes) Copy(r []rune) []rune {
+	n := make([]rune, len(r))
+	copy(n, r)
+	return n
+}
+
+func (Runes) HasPrefix(r, prefix []rune) bool {
+	if len(r) < len(prefix) {
+		return false
+	}
+	return runes.Equal(r[:len(prefix)], prefix)
+}
+
+func (Runes) Aggregate(candicate [][]rune) (same []rune, size int) {
+	for i := 0; i < len(candicate[0]); i++ {
+		for j := 0; j < len(candicate)-1; j++ {
+			if i >= len(candicate[j]) || i >= len(candicate[j+1]) {
+				goto aggregate
+			}
+			if candicate[j][i] != candicate[j+1][i] {
+				goto aggregate
+			}
+		}
+		size = i + 1
+	}
+aggregate:
+	if size > 0 {
+		same = runes.Copy(candicate[0][:size])
+		for i := 0; i < len(candicate); i++ {
+			n := runes.Copy(candicate[i])
+			copy(n, n[size:])
+			candicate[i] = n[:len(n)-size]
+		}
+	}
+	return
+}
+
+func (Runes) TrimSpaceLeft(in []rune) []rune {
+	firstIndex := len(in)
+	for i, r := range in {
+		if unicode.IsSpace(r) == false {
+			firstIndex = i
+			break
+		}
+	}
+	return in[firstIndex:]
+}
diff --git a/vendor/gopkg.in/readline.v1/runes/runes.go b/vendor/gopkg.in/readline.v1/runes/runes.go
new file mode 100644
index 0000000..b069440
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/runes/runes.go
@@ -0,0 +1,155 @@
+// deprecated.
+// see https://github.com/chzyer/readline/issues/43
+// use github.com/chzyer/readline/runes.go
+package runes
+
+import (
+	"bytes"
+	"unicode"
+)
+
+func Equal(a, b []rune) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i := 0; i < len(a); i++ {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+	return true
+}
+
+// Search in runes from end to front
+func IndexAllBck(r, sub []rune) int {
+	for i := len(r) - len(sub); i >= 0; i-- {
+		found := true
+		for j := 0; j < len(sub); j++ {
+			if r[i+j] != sub[j] {
+				found = false
+				break
+			}
+		}
+		if found {
+			return i
+		}
+	}
+	return -1
+}
+
+// Search in runes from front to end
+func IndexAll(r, sub []rune) int {
+	for i := 0; i < len(r); i++ {
+		found := true
+		if len(r[i:]) < len(sub) {
+			return -1
+		}
+		for j := 0; j < len(sub); j++ {
+			if r[i+j] != sub[j] {
+				found = false
+				break
+			}
+		}
+		if found {
+			return i
+		}
+	}
+	return -1
+}
+
+func Index(r rune, rs []rune) int {
+	for i := 0; i < len(rs); i++ {
+		if rs[i] == r {
+			return i
+		}
+	}
+	return -1
+}
+
+func ColorFilter(r []rune) []rune {
+	newr := make([]rune, 0, len(r))
+	for pos := 0; pos < len(r); pos++ {
+		if r[pos] == '\033' && r[pos+1] == '[' {
+			idx := Index('m', r[pos+2:])
+			if idx == -1 {
+				continue
+			}
+			pos += idx + 2
+			continue
+		}
+		newr = append(newr, r[pos])
+	}
+	return newr
+}
+
+var zeroWidth = []*unicode.RangeTable{
+	unicode.Mn,
+	unicode.Me,
+	unicode.Cc,
+	unicode.Cf,
+}
+
+var doubleWidth = []*unicode.RangeTable{
+	unicode.Han,
+	unicode.Hangul,
+	unicode.Hiragana,
+	unicode.Katakana,
+}
+
+func Width(r rune) int {
+	if unicode.IsOneOf(zeroWidth, r) {
+		return 0
+	}
+	if unicode.IsOneOf(doubleWidth, r) {
+		return 2
+	}
+	return 1
+}
+
+func WidthAll(r []rune) (length int) {
+	for i := 0; i < len(r); i++ {
+		length += Width(r[i])
+	}
+	return
+}
+
+func Backspace(r []rune) []byte {
+	return bytes.Repeat([]byte{'\b'}, WidthAll(r))
+}
+
+func Copy(r []rune) []rune {
+	n := make([]rune, len(r))
+	copy(n, r)
+	return n
+}
+
+func HasPrefix(r, prefix []rune) bool {
+	if len(r) < len(prefix) {
+		return false
+	}
+	return Equal(r[:len(prefix)], prefix)
+}
+
+func Aggregate(candicate [][]rune) (same []rune, size int) {
+	for i := 0; i < len(candicate[0]); i++ {
+		for j := 0; j < len(candicate)-1; j++ {
+			if i >= len(candicate[j]) || i >= len(candicate[j+1]) {
+				goto aggregate
+			}
+			if candicate[j][i] != candicate[j+1][i] {
+				goto aggregate
+			}
+		}
+		size = i + 1
+	}
+aggregate:
+	if size > 0 {
+		same = Copy(candicate[0][:size])
+		for i := 0; i < len(candicate); i++ {
+			n := Copy(candicate[i])
+			copy(n, n[size:])
+			candicate[i] = n[:len(n)-size]
+		}
+	}
+	return
+}
diff --git a/vendor/gopkg.in/readline.v1/runes/runes_test.go b/vendor/gopkg.in/readline.v1/runes/runes_test.go
new file mode 100644
index 0000000..b7a1412
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/runes/runes_test.go
@@ -0,0 +1,68 @@
+package runes
+
+import (
+	"reflect"
+	"testing"
+)
+
+type twidth struct {
+	r      []rune
+	length int
+}
+
+func TestRuneWidth(t *testing.T) {
+	runes := []twidth{
+		{[]rune("?"), 1},
+		{[]rune("a"), 1},
+		{[]rune("?"), 2},
+		{ColorFilter([]rune("?\033[13;1m?")), 3},
+	}
+	for _, r := range runes {
+		if w := WidthAll(r.r); w != r.length {
+			t.Fatal("result not expect", r.r, r.length, w)
+		}
+	}
+}
+
+type tagg struct {
+	r      [][]rune
+	e      [][]rune
+	length int
+}
+
+func TestAggRunes(t *testing.T) {
+	runes := []tagg{
+		{
+			[][]rune{[]rune("ab"), []rune("a"), []rune("abc")},
+			[][]rune{[]rune("b"), []rune(""), []rune("bc")},
+			1,
+		},
+		{
+			[][]rune{[]rune("addb"), []rune("ajkajsdf"), []rune("aasdfkc")},
+			[][]rune{[]rune("ddb"), []rune("jkajsdf"), []rune("asdfkc")},
+			1,
+		},
+		{
+			[][]rune{[]rune("ddb"), []rune("ajksdf"), []rune("aasdfkc")},
+			[][]rune{[]rune("ddb"), []rune("ajksdf"), []rune("aasdfkc")},
+			0,
+		},
+		{
+			[][]rune{[]rune("ddb"), []rune("ddajksdf"), []rune("ddaasdfkc")},
+			[][]rune{[]rune("b"), []rune("ajksdf"), []rune("aasdfkc")},
+			2,
+		},
+	}
+	for _, r := range runes {
+		same, off := Aggregate(r.r)
+		if off != r.length {
+			t.Fatal("result not expect", off)
+		}
+		if len(same) != off {
+			t.Fatal("result not expect", same)
+		}
+		if !reflect.DeepEqual(r.r, r.e) {
+			t.Fatal("result not expect")
+		}
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/runes_test.go b/vendor/gopkg.in/readline.v1/runes_test.go
new file mode 100644
index 0000000..9c56d79
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/runes_test.go
@@ -0,0 +1,68 @@
+package readline
+
+import (
+	"reflect"
+	"testing"
+)
+
+type twidth struct {
+	r      []rune
+	length int
+}
+
+func TestRuneWidth(t *testing.T) {
+	rs := []twidth{
+		{[]rune("?"), 1},
+		{[]rune("a"), 1},
+		{[]rune("?"), 2},
+		{runes.ColorFilter([]rune("?\033[13;1m?")), 3},
+	}
+	for _, r := range rs {
+		if w := runes.WidthAll(r.r); w != r.length {
+			t.Fatal("result not expect", r.r, r.length, w)
+		}
+	}
+}
+
+type tagg struct {
+	r      [][]rune
+	e      [][]rune
+	length int
+}
+
+func TestAggRunes(t *testing.T) {
+	rs := []tagg{
+		{
+			[][]rune{[]rune("ab"), []rune("a"), []rune("abc")},
+			[][]rune{[]rune("b"), []rune(""), []rune("bc")},
+			1,
+		},
+		{
+			[][]rune{[]rune("addb"), []rune("ajkajsdf"), []rune("aasdfkc")},
+			[][]rune{[]rune("ddb"), []rune("jkajsdf"), []rune("asdfkc")},
+			1,
+		},
+		{
+			[][]rune{[]rune("ddb"), []rune("ajksdf"), []rune("aasdfkc")},
+			[][]rune{[]rune("ddb"), []rune("ajksdf"), []rune("aasdfkc")},
+			0,
+		},
+		{
+			[][]rune{[]rune("ddb"), []rune("ddajksdf"), []rune("ddaasdfkc")},
+			[][]rune{[]rune("b"), []rune("ajksdf"), []rune("aasdfkc")},
+			2,
+		},
+	}
+	for _, r := range rs {
+		same, off := runes.Aggregate(r.r)
+		if off != r.length {
+			t.Fatal("result not expect", off)
+		}
+		if len(same) != off {
+			t.Fatal("result not expect", same)
+		}
+		if !reflect.DeepEqual(r.r, r.e) {
+			t.Fatal("result not expect")
+		}
+	}
+}
diff --git a/vendor/gopkg.in/readline.v1/search.go b/vendor/gopkg.in/readline.v1/search.go
new file mode 100644
index 0000000..2f20eb2
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/search.go
@@ -0,0 +1,160 @@
+package readline
+
+import (
+	"bytes"
+	"container/list"
+	"fmt"
+	"io"
+)
+
+const (
+	S_STATE_FOUND = iota
+	S_STATE_FAILING
+)
+
+const (
+	S_DIR_BCK = iota
+	S_DIR_FWD
+)
+
+type opSearch struct {
+	inMode    bool
+	state     int
+	dir       int
+	source    *list.Element
+	w         io.Writer
+	buf       *RuneBuffer
+	data      []rune
+	history   *opHistory
+	cfg       *Config
+	markStart int
+	markEnd   int
+	width     int
+}
+
+func newOpSearch(w io.Writer, buf *RuneBuffer, history *opHistory, cfg *Config, width int) *opSearch {
+	return &opSearch{
+		w:       w,
+		buf:     buf,
+		cfg:     cfg,
+		history: history,
+		width:   width,
+	}
+}
+
+func (o *opSearch) OnWidthChange(newWidth int) {
+	o.width = newWidth
+}
+
+func (o *opSearch) IsSearchMode() bool {
+	return o.inMode
+}
+
+func (o *opSearch) SearchBackspace() {
+	if len(o.data) > 0 {
+		o.data = o.data[:len(o.data)-1]
+		o.search(true)
+	}
+}
+
+func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
+	if o.dir == S_DIR_BCK {
+		return o.history.FindBck(isNewSearch, o.data, o.buf.idx)
+	}
+	return o.history.FindFwd(isNewSearch, o.data, o.buf.idx)
+}
+
+func (o *opSearch) search(isChange bool) bool {
+	if len(o.data) == 0 {
+		o.state = S_STATE_FOUND
+		o.SearchRefresh(-1)
+		return true
+	}
+	idx, elem := o.findHistoryBy(isChange)
+	if elem == nil {
+		o.SearchRefresh(-2)
+		return false
+	}
+	o.history.current = elem
+
+	item := o.history.showItem(o.history.current.Value)
+	start, end := 0, 0
+	if o.dir == S_DIR_BCK {
+		start, end = idx, idx+len(o.data)
+	} else {
+		start, end = idx, idx+len(o.data)
+		idx += len(o.data)
+	}
+	o.buf.SetWithIdx(idx, item)
+	o.markStart, o.markEnd = start, end
+	o.SearchRefresh(idx)
+	return true
+}
+
+func (o *opSearch) SearchChar(r rune) {
+	o.data = append(o.data, r)
+	o.search(true)
+}
+
+func (o *opSearch) SearchMode(dir int) {
+	alreadyInMode := o.inMode
+	o.inMode = true
+	o.dir = dir
+	o.source = o.history.current
+	if alreadyInMode {
+		o.search(false)
+	} else {
+		o.SearchRefresh(-1)
+	}
+}
+
+func (o *opSearch) ExitSearchMode(revert bool) {
+	if revert {
+		o.history.current = o.source
+		o.buf.Set(o.history.showItem(o.history.current.Value))
+	}
+	o.markStart, o.markEnd = 0, 0
+	o.state = S_STATE_FOUND
+	o.inMode = false
+	o.source = nil
+	o.data = nil
+}
+
+func (o *opSearch) SearchRefresh(x int) {
+	if x == -2 {
+		o.state = S_STATE_FAILING
+	} else if x >= 0 {
+		o.state = S_STATE_FOUND
+	}
+	if x < 0 {
+		x = o.buf.idx
+	}
+	x = o.buf.CurrentWidth(x)
+	x += o.buf.PromptLen()
+	x = x % o.width
+
+	if o.markStart > 0 {
+		o.buf.SetStyle(o.markStart, o.markEnd, "4")
+	}
+
+	lineCnt := o.buf.CursorLineCount()
+	buf := bytes.NewBuffer(nil)
+	buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
+	buf.WriteString("\033[J")
+	if o.state == S_STATE_FAILING {
+		buf.WriteString("failing ")
+	}
+	if o.dir == S_DIR_BCK {
+		buf.WriteString("bck")
+	} else if o.dir == S_DIR_FWD {
+		buf.WriteString("fwd")
+	}
+	buf.WriteString("-i-search: ")
+	buf.WriteString(string(o.data))         // keyword
+	buf.WriteString("\033[4m \033[0m")      // _
+	fmt.Fprintf(buf, "\r\033[%dA", lineCnt) // move prev
+	if x > 0 {
+		fmt.Fprintf(buf, "\033[%dC", x) // move forward
+	}
+	o.w.Write(buf.Bytes())
+}
diff --git a/vendor/gopkg.in/readline.v1/std.go b/vendor/gopkg.in/readline.v1/std.go
new file mode 100644
index 0000000..eb72882
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/std.go
@@ -0,0 +1,66 @@
+package readline
+
+import (
+	"io"
+	"os"
+	"sync"
+)
+
+var (
+	Stdin  io.ReadCloser  = os.Stdin
+	Stdout io.WriteCloser = os.Stdout
+	Stderr io.WriteCloser = os.Stderr
+)
+
+var (
+	std     *Instance
+	stdOnce sync.Once
+)
+
+// global instance will not submit history automatic
+func getInstance() *Instance {
+	stdOnce.Do(func() {
+		std, _ = NewEx(&Config{
+			DisableAutoSaveHistory: true,
+		})
+	})
+	return std
+}
+
+// let readline load history from filepath
+// and try to persist history into disk
+// set fp to "" to prevent readline persisting history to disk
+// so the `AddHistory` will return nil error forever.
+func SetHistoryPath(fp string) {
+	ins := getInstance()
+	cfg := ins.Config.Clone()
+	cfg.HistoryFile = fp
+	ins.SetConfig(cfg)
+}
+
+// set auto completer to global instance
+func SetAutoComplete(completer AutoCompleter) {
+	ins := getInstance()
+	cfg := ins.Config.Clone()
+	cfg.AutoComplete = completer
+	ins.SetConfig(cfg)
+}
+
+// add history to global instance manually
+// raise error only if `SetHistoryPath` is set with a non-empty path
+func AddHistory(content string) error {
+	ins := getInstance()
+	return ins.SaveHistory(content)
+}
+
+func Password(prompt string) ([]byte, error) {
+	ins := getInstance()
+	return ins.ReadPassword(prompt)
+}
+
+// readline with global configs
+func Line(prompt string) (string, error) {
+	ins := getInstance()
+	ins.SetPrompt(prompt)
+	return ins.Readline()
+}
diff --git a/vendor/gopkg.in/readline.v1/std_windows.go b/vendor/gopkg.in/readline.v1/std_windows.go
new file mode 100644
index 0000000..b10f91b
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/std_windows.go
@@ -0,0 +1,9 @@
+// +build windows
+
+package readline
+
+func init() {
+	Stdin = NewRawReader()
+	Stdout = NewANSIWriter(Stdout)
+	Stderr = NewANSIWriter(Stderr)
+}
diff --git a/vendor/gopkg.in/readline.v1/term.go b/vendor/gopkg.in/readline.v1/term.go
new file mode 100644
index 0000000..87ef8f7
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/term.go
@@ -0,0 +1,134 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// 	oldState, err := terminal.MakeRaw(0)
+// 	if err != nil {
+// 	        panic(err)
+// 	}
+// 	defer terminal.Restore(0, oldState)
+package readline
+
+import (
+	"io"
+	"syscall"
+	"unsafe"
+)
+
+// State contains the state of a terminal.
+type State struct {
+	termios syscall.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	var termios syscall.Termios
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+	return err == 0
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	var oldState State
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
+		return nil, err
+	}
+
+	newState := oldState.termios
+	// This attempts to replicate the behaviour documented for cfmakeraw in
+	// the termios(3) manpage.
+	newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
+	// newState.Oflag &^= syscall.OPOST
+	newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
+	newState.Cflag &^= syscall.CSIZE | syscall.PARENB
+	newState.Cflag |= syscall.CS8
+
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
+		return nil, err
+	}
+
+	return &oldState, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	var oldState State
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
+		return nil, err
+	}
+
+	return &oldState, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func restoreTerm(fd int, state *State) error {
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
+	return err
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	var dimensions [4]uint16
+
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 {
+		return -1, -1, err
+	}
+	return int(dimensions[1]), int(dimensions[0]), nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	var oldState syscall.Termios
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 {
+		return nil, err
+	}
+
+	newState := oldState
+	newState.Lflag &^= syscall.ECHO
+	newState.Lflag |= syscall.ICANON | syscall.ISIG
+	newState.Iflag |= syscall.ICRNL
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
+		return nil, err
+	}
+
+	defer func() {
+		syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0)
+	}()
+
+	var buf [16]byte
+	var ret []byte
+	for {
+		n, err := syscall.Read(fd, buf[:])
+		if err != nil {
+			return nil, err
+		}
+		if n == 0 {
+			if len(ret) == 0 {
+				return nil, io.EOF
+			}
+			break
+		}
+		if buf[n-1] == '\n' {
+			n--
+		}
+		ret = append(ret, buf[:n]...)
+		if n < len(buf) {
+			break
+		}
+	}
+
+	return ret, nil
+}
diff --git a/vendor/gopkg.in/readline.v1/term_bsd.go b/vendor/gopkg.in/readline.v1/term_bsd.go
new file mode 100644
index 0000000..69682cd
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/term_bsd.go
@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package readline
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TIOCGETA
+const ioctlWriteTermios = syscall.TIOCSETA
diff --git a/vendor/gopkg.in/readline.v1/term_linux.go b/vendor/gopkg.in/readline.v1/term_linux.go
new file mode 100644
index 0000000..8918008
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/term_linux.go
@@ -0,0 +1,11 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package readline
+
+// These constants are declared here, rather than importing
+// them from the syscall package as some syscall packages, even
+// on linux, for example gccgo, do not declare them.
+const ioctlReadTermios = 0x5401  // syscall.TCGETS
+const ioctlWriteTermios = 0x5402 // syscall.TCSETS
diff --git a/vendor/gopkg.in/readline.v1/term_windows.go b/vendor/gopkg.in/readline.v1/term_windows.go
new file mode 100644
index 0000000..1290e00
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/term_windows.go
@@ -0,0 +1,171 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// 	oldState, err := terminal.MakeRaw(0)
+// 	if err != nil {
+// 	        panic(err)
+// 	}
+// 	defer terminal.Restore(0, oldState)
+package readline
+
+import (
+	"io"
+	"syscall"
+	"unsafe"
+)
+
+const (
+	enableLineInput       = 2
+	enableEchoInput       = 4
+	enableProcessedInput  = 1
+	enableWindowInput     = 8
+	enableMouseInput      = 16
+	enableInsertMode      = 32
+	enableQuickEditMode   = 64
+	enableExtendedFlags   = 128
+	enableAutoPosition    = 256
+	enableProcessedOutput = 1
+	enableWrapAtEolOutput = 2
+)
+
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+var (
+	procGetConsoleMode             = kernel32.NewProc("GetConsoleMode")
+	procSetConsoleMode             = kernel32.NewProc("SetConsoleMode")
+	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+)
+
+type (
+	coord struct {
+		x short
+		y short
+	}
+	smallRect struct {
+		left   short
+		top    short
+		right  short
+		bottom short
+	}
+	consoleScreenBufferInfo struct {
+		size              coord
+		cursorPosition    coord
+		attributes        word
+		window            smallRect
+		maximumWindowSize coord
+	}
+)
+
+type State struct {
+	mode uint32
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	var st uint32
+	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+	return r != 0 && e == 0
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	var st uint32
+	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+	if e != 0 {
+		return nil, error(e)
+	}
+	raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
+	_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
+	if e != 0 {
+		return nil, error(e)
+	}
+	return &State{st}, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	var st uint32
+	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+	if e != 0 {
+		return nil, error(e)
+	}
+	return &State{st}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func restoreTerm(fd int, state *State) error {
+	_, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
+	return err
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	var info consoleScreenBufferInfo
+	_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
+	if e != 0 {
+		return 0, 0, error(e)
+	}
+	return int(info.size.x), int(info.size.y), nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	var st uint32
+	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+	if e != 0 {
+		return nil, error(e)
+	}
+	old := st
+
+	st &^= (enableEchoInput)
+	st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
+	_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
+	if e != 0 {
+		return nil, error(e)
+	}
+
+	defer func() {
+		syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
+	}()
+
+	var buf [16]byte
+	var ret []byte
+	for {
+		n, err := syscall.Read(syscall.Handle(fd), buf[:])
+		if err != nil {
+			return nil, err
+		}
+		if n == 0 {
+			if len(ret) == 0 {
+				return nil, io.EOF
+			}
+			break
+		}
+		if buf[n-1] == '\n' {
+			n--
+		}
+		if n > 0 && buf[n-1] == '\r' {
+			n--
+		}
+		ret = append(ret, buf[:n]...)
+		if n < len(buf) {
+			break
+		}
+	}
+
+	return ret, nil
+}
diff --git a/vendor/gopkg.in/readline.v1/terminal.go b/vendor/gopkg.in/readline.v1/terminal.go
new file mode 100644
index 0000000..0f1d393
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/terminal.go
@@ -0,0 +1,175 @@
+package readline
+
+import (
+	"bufio"
+	"fmt"
+	"strings"
+	"sync"
+	"sync/atomic"
+)
+
+type Terminal struct {
+	cfg       *Config
+	outchan   chan rune
+	closed    int32
+	stopChan  chan struct{}
+	kickChan  chan struct{}
+	wg        sync.WaitGroup
+	isReading int32
+	sleeping  int32
+}
+
+func NewTerminal(cfg *Config) (*Terminal, error) {
+	if err := cfg.Init(); err != nil {
+		return nil, err
+	}
+	t := &Terminal{
+		cfg:      cfg,
+		kickChan: make(chan struct{}, 1),
+		outchan:  make(chan rune),
+		stopChan: make(chan struct{}, 1),
+	}
+
+	go t.ioloop()
+	return t, nil
+}
+
+// SleepToResume will sleep myself, and return only if I'm resumed.
+func (t *Terminal) SleepToResume() {
+	if !atomic.CompareAndSwapInt32(&t.sleeping, 0, 1) {
+		return
+	}
+	defer atomic.StoreInt32(&t.sleeping, 0)
+
+	t.ExitRawMode()
+	ch := WaitForResume()
+	SuspendMe()
+	<-ch
+	t.EnterRawMode()
+}
+
+func (t *Terminal) EnterRawMode() (err error) {
+	return t.cfg.FuncMakeRaw()
+}
+
+func (t *Terminal) ExitRawMode() (err error) {
+	return t.cfg.FuncExitRaw()
+}
+
+func (t *Terminal) Write(b []byte) (int, error) {
+	return t.cfg.Stdout.Write(b)
+}
+
+func (t *Terminal) Print(s string) {
+	fmt.Fprintf(t.cfg.Stdout, "%s", s)
+}
+
+func (t *Terminal) PrintRune(r rune) {
+	fmt.Fprintf(t.cfg.Stdout, "%c", r)
+}
+
+func (t *Terminal) Readline() *Operation {
+	return NewOperation(t, t.cfg)
+}
+
+// return rune(0) if meet EOF
+func (t *Terminal) ReadRune() rune {
+	ch, ok := <-t.outchan
+	if !ok {
+		return rune(0)
+	}
+	return ch
+}
+
+func (t *Terminal) IsReading() bool {
+	return atomic.LoadInt32(&t.isReading) == 1
+}
+
+func (t *Terminal) KickRead() {
+	select {
+	case t.kickChan <- struct{}{}:
+	default:
+	}
+}
+
+func (t *Terminal) ioloop() {
+	t.wg.Add(1)
+	defer t.wg.Done()
+	var (
+		isEscape       bool
+		isEscapeEx     bool
+		expectNextChar bool
+	)
+
+	buf := bufio.NewReader(t.cfg.Stdin)
+	for {
+		if !expectNextChar {
+			atomic.StoreInt32(&t.isReading, 0)
+			select {
+			case <-t.kickChan:
+				atomic.StoreInt32(&t.isReading, 1)
+			case <-t.stopChan:
+				return
+			}
+		}
+		expectNextChar = false
+		r, _, err := buf.ReadRune()
+		if err != nil {
+			if strings.Contains(err.Error(), "interrupted system call") {
+				expectNextChar = true
+				continue
+			}
+			break
+		}
+
+		if isEscape {
+			isEscape = false
+			if r == CharEscapeEx {
+				expectNextChar = true
+				isEscapeEx = true
+				continue
+			}
+			r = escapeKey(r, buf)
+		} else if isEscapeEx {
+			isEscapeEx = false
+			r = escapeExKey(r, buf)
+		}
+
+		expectNextChar = true
+		switch r {
+		case CharEsc:
+			if t.cfg.VimMode {
+				t.outchan <- r
+				break
+			}
+			isEscape = true
+		case CharInterrupt, CharEnter, CharCtrlJ, CharDelete:
+			expectNextChar = false
+			fallthrough
+		default:
+			t.outchan <- r
+		}
+	}
+	close(t.outchan)
+}
+
+func (t *Terminal) Bell() {
+	fmt.Fprintf(t, "%c", CharBell)
+}
+
+func (t *Terminal) Close() error {
+	if atomic.SwapInt32(&t.closed, 1) != 0 {
+		return nil
+	}
+	t.stopChan <- struct{}{}
+	t.wg.Wait()
+	return t.ExitRawMode()
+}
+
+func (t *Terminal) SetConfig(c *Config) error {
+	if err := c.Init(); err != nil {
+		return err
+	}
+	t.cfg = c
+	return nil
+}
diff --git a/vendor/gopkg.in/readline.v1/utils.go b/vendor/gopkg.in/readline.v1/utils.go
new file mode 100644
index 0000000..c0031a8
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/utils.go
@@ -0,0 +1,176 @@
+package readline
+
+import (
+	"bufio"
+	"bytes"
+	"strconv"
+	"sync"
+	"time"
+)
+
+var (
+	isWindows = false
+)
+
+// WaitForResume need to call before current process got suspend.
+// It will run a ticker until a long duration is occurs,
+// which means this process is resumed.
+func WaitForResume() chan struct{} {
+	ch := make(chan struct{})
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		ticker := time.NewTicker(10 * time.Millisecond)
+		t := time.Now()
+		wg.Done()
+		for {
+			now := <-ticker.C
+			if now.Sub(t) > 100*time.Millisecond {
+				break
+			}
+			t = now
+		}
+		ticker.Stop()
+		ch <- struct{}{}
+	}()
+	wg.Wait()
+	return ch
+}
+
+func Restore(fd int, state *State) error {
+	err := restoreTerm(fd, state)
+	if err != nil {
+		// errno 0 means everything is ok :)
+		if err.Error() == "errno 0" {
+			err = nil
+		}
+	}
+	return nil
+}
+
+func IsPrintable(key rune) bool {
+	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
+	return key >= 32 && !isInSurrogateArea
+}
+
+// translate Esc[X
+func escapeExKey(r rune, reader *bufio.Reader) rune {
+	switch r {
+	case 'D':
+		r = CharBackward
+	case 'C':
+		r = CharForward
+	case 'A':
+		r = CharPrev
+	case 'B':
+		r = CharNext
+	case 'H':
+		r = CharLineStart
+	case 'F':
+		r = CharLineEnd
+	default:
+		if r == '3' && reader != nil {
+			d, _, _ := reader.ReadRune()
+			if d == '~' {
+				r = CharDelete
+			} else {
+				reader.UnreadRune()
+			}
+		}
+	}
+	return r
+}
+
+// translate EscX to Meta+X
+func escapeKey(r rune, reader *bufio.Reader) rune {
+	switch r {
+	case 'b':
+		r = MetaBackward
+	case 'f':
+		r = MetaForward
+	case 'd':
+		r = MetaDelete
+	case CharTranspose:
+		r = MetaTranspose
+	case CharBackspace:
+		r = MetaBackspace
+	case 'O':
+		d, _, _ := reader.ReadRune()
+		switch d {
+		case 'H':
+			r = CharLineStart
+		case 'F':
+			r = CharLineEnd
+		default:
+			reader.UnreadRune()
+		}
+	case CharEsc:
+
+	}
+	return r
+}
+
+func SplitByLine(start, screenWidth int, rs []rune) []string {
+	var ret []string
+	buf := bytes.NewBuffer(nil)
+	currentWidth := start
+	for _, r := range rs {
+		w := runes.Width(r)
+		currentWidth += w
+		buf.WriteRune(r)
+		if currentWidth >= screenWidth {
+			ret = append(ret, buf.String())
+			buf.Reset()
+			currentWidth = 0
+		}
+	}
+	ret = append(ret, buf.String())
+	return ret
+}
+
+// calculate how many lines for N character
+func LineCount(screenWidth, w int) int {
+	r := w / screenWidth
+	if w%screenWidth != 0 {
+		r++
+	}
+	return r
+}
+
+func IsWordBreak(i rune) bool {
+	switch {
+	case i >= 'a' && i <= 'z':
+	case i >= 'A' && i <= 'Z':
+	case i >= '0' && i <= '9':
+	default:
+		return true
+	}
+	return false
+}
+
+func GetInt(s []string, def int) int {
+	if len(s) == 0 {
+		return def
+	}
+	c, err := strconv.Atoi(s[0])
+	if err != nil {
+		return def
+	}
+	return c
+}
+
+type RawMode struct {
+	state *State
+}
+
+func (r *RawMode) Enter() (err error) {
+	r.state, err = MakeRaw(GetStdin())
+	return err
+}
+
+func (r *RawMode) Exit() error {
+	if r.state == nil {
+		return nil
+	}
+	return Restore(GetStdin(), r.state)
+}
diff --git a/vendor/gopkg.in/readline.v1/utils_test.go b/vendor/gopkg.in/readline.v1/utils_test.go
new file mode 100644
index 0000000..96037df
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/utils_test.go
@@ -0,0 +1 @@
+package readline
diff --git a/vendor/gopkg.in/readline.v1/utils_unix.go b/vendor/gopkg.in/readline.v1/utils_unix.go
new file mode 100644
index 0000000..39c32a1
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/utils_unix.go
@@ -0,0 +1,90 @@
+// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
+
+package readline
+
+import (
+	"io"
+	"os"
+	"os/signal"
+	"sync"
+	"syscall"
+	"unsafe"
+)
+
+type winsize struct {
+	Row    uint16
+	Col    uint16
+	Xpixel uint16
+	Ypixel uint16
+}
+
+// SuspendMe use to send suspend signal to myself, when we in the raw mode.
+// For OSX it need to send to parent's pid
+// For Linux it need to send to myself
+func SuspendMe() {
+	p, _ := os.FindProcess(os.Getppid())
+	p.Signal(syscall.SIGTSTP)
+	p, _ = os.FindProcess(os.Getpid())
+	p.Signal(syscall.SIGTSTP)
+}
+
+// get width of the terminal
+func getWidth(stdoutFd int) int {
+	ws := &winsize{}
+	retCode, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
+		uintptr(stdoutFd),
+		uintptr(syscall.TIOCGWINSZ),
+		uintptr(unsafe.Pointer(ws)))
+
+	if int(retCode) == -1 {
+		_ = errno
+		return -1
+	}
+	return int(ws.Col)
+}
+
+func GetScreenWidth() int {
+	w := getWidth(syscall.Stdout)
+	if w < 0 {
+		w = getWidth(syscall.Stderr)
+	}
+	return w
+}
+
+// ClearScreen clears the console screen
+func ClearScreen(w io.Writer) (int, error) {
+	return w.Write([]byte("\033[H"))
+}
+
+func DefaultIsTerminal() bool {
+	return IsTerminal(syscall.Stdin) && (IsTerminal(syscall.Stdout) || IsTerminal(syscall.Stderr))
+}
+
+func GetStdin() int {
+	return syscall.Stdin
+}
+
+// -----------------------------------------------------------------------------
+
+var (
+	widthChange         sync.Once
+	widthChangeCallback func()
+)
+
+func DefaultOnWidthChanged(f func()) {
+	widthChangeCallback = f
+	widthChange.Do(func() {
+		ch := make(chan os.Signal, 1)
+		signal.Notify(ch, syscall.SIGWINCH)
+
+		go func() {
+			for {
+				_, ok := <-ch
+				if !ok {
+					break
+				}
+				widthChangeCallback()
+			}
+		}()
+	})
+}
diff --git a/vendor/gopkg.in/readline.v1/utils_windows.go b/vendor/gopkg.in/readline.v1/utils_windows.go
new file mode 100644
index 0000000..5bfa55d
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/utils_windows.go
@@ -0,0 +1,41 @@
+// +build windows
+
+package readline
+
+import (
+	"io"
+	"syscall"
+)
+
+func SuspendMe() {
+}
+
+func GetStdin() int {
+	return int(syscall.Stdin)
+}
+
+func init() {
+	isWindows = true
+}
+
+// get width of the terminal
+func GetScreenWidth() int {
+	info, _ := GetConsoleScreenBufferInfo()
+	if info == nil {
+		return -1
+	}
+	return int(info.dwSize.x)
+}
+
+// ClearScreen clears the console screen
+func ClearScreen(_ io.Writer) error {
+	return SetConsoleCursorPosition(&_COORD{0, 0})
+}
+
+func DefaultIsTerminal() bool {
+	return true
+}
+
+func DefaultOnWidthChanged(func()) {
+
+}
diff --git a/vendor/gopkg.in/readline.v1/vim.go b/vendor/gopkg.in/readline.v1/vim.go
new file mode 100644
index 0000000..641b22b
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/vim.go
@@ -0,0 +1,174 @@
+package readline
+
+const (
+	VIM_NORMAL = iota
+	VIM_INSERT
+	VIM_VISUAL
+)
+
+type opVim struct {
+	cfg     *Config
+	op      *Operation
+	vimMode int
+}
+
+func newVimMode(op *Operation) *opVim {
+	ov := &opVim{
+		cfg: op.cfg,
+		op:  op,
+	}
+	ov.SetVimMode(ov.cfg.VimMode)
+	return ov
+}
+
+func (o *opVim) SetVimMode(on bool) {
+	if o.cfg.VimMode && !on { // turn off
+		o.ExitVimMode()
+	}
+	o.cfg.VimMode = on
+	o.vimMode = VIM_INSERT
+}
+
+func (o *opVim) ExitVimMode() {
+	o.vimMode = VIM_INSERT
+}
+
+func (o *opVim) IsEnableVimMode() bool {
+	return o.cfg.VimMode
+}
+
+func (o *opVim) handleVimNormalMovement(r rune, readNext func() rune) (t rune, handled bool) {
+	rb := o.op.buf
+	handled = true
+	switch r {
+	case 'h':
+		t = CharBackward
+	case 'j':
+		t = CharNext
+	case 'k':
+		t = CharPrev
+	case 'l':
+		t = CharForward
+	case '0', '^':
+		rb.MoveToLineStart()
+	case '$':
+		rb.MoveToLineEnd()
+	case 'x':
+		rb.Delete()
+		if rb.IsCursorInEnd() {
+			rb.MoveBackward()
+		}
+	case 'r':
+		rb.Replace(readNext())
+	case 'd':
+		next := readNext()
+		switch next {
+		case 'd':
+			rb.Erase()
+		case 'w':
+			rb.DeleteWord()
+		case 'h':
+			rb.Backspace()
+		case 'l':
+			rb.Delete()
+		}
+	case 'b', 'B':
+		rb.MoveToPrevWord()
+	case 'w', 'W':
+		rb.MoveToNextWord()
+	case 'e', 'E':
+		rb.MoveToEndWord()
+	case 'f', 'F', 't', 'T':
+		next := readNext()
+		prevChar := r == 't' || r == 'T'
+		reverse := r == 'F' || r == 'T'
+		switch next {
+		case CharEsc:
+		default:
+			rb.MoveTo(next, prevChar, reverse)
+		}
+	default:
+		return r, false
+	}
+	return t, true
+}
+
+func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() rune) (t rune, handled bool) {
+	rb := o.op.buf
+	handled = true
+	switch r {
+	case 'i':
+	case 'I':
+		rb.MoveToLineStart()
+	case 'a':
+		rb.MoveForward()
+	case 'A':
+		rb.MoveToLineEnd()
+	case 's':
+		rb.Delete()
+	case 'S':
+		rb.Erase()
+	case 'c':
+		next := readNext()
+		switch next {
+		case 'c':
+			rb.Erase()
+		case 'w':
+			rb.DeleteWord()
+		case 'h':
+			rb.Backspace()
+		case 'l':
+			rb.Delete()
+		}
+	default:
+		return r, false
+	}
+
+	o.EnterVimInsertMode()
+	return
+}
+
+func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune) {
+	switch r {
+	case CharEnter, CharInterrupt:
+		o.ExitVimMode()
+		return r
+	}
+
+	if r, handled := o.handleVimNormalMovement(r, readNext); handled {
+		return r
+	}
+
+	if r, handled := o.handleVimNormalEnterInsert(r, readNext); handled {
+		return r
+	}
+
+	// invalid operation
+	o.op.t.Bell()
+	return 0
+}
+
+func (o *opVim) EnterVimInsertMode() {
+	o.vimMode = VIM_INSERT
+}
+
+func (o *opVim) ExitVimInsertMode() {
+	o.vimMode = VIM_NORMAL
+}
+
+func (o *opVim) HandleVim(r rune, readNext func() rune) rune {
+	if o.vimMode == VIM_NORMAL {
+		return o.HandleVimNormal(r, readNext)
+	}
+	if r == CharEsc {
+		o.ExitVimInsertMode()
+		return 0
+	}
+
+	switch o.vimMode {
+	case VIM_INSERT:
+		return r
+	case VIM_VISUAL:
+	}
+	return r
+}
diff --git a/vendor/gopkg.in/readline.v1/windows_api.go b/vendor/gopkg.in/readline.v1/windows_api.go
new file mode 100644
index 0000000..63f4f7b
--- /dev/null
+++ b/vendor/gopkg.in/readline.v1/windows_api.go
@@ -0,0 +1,152 @@
+// +build windows
+
+package readline
+
+import (
+	"reflect"
+	"syscall"
+	"unsafe"
+)
+
+var (
+	kernel = NewKernel()
+	stdout = uintptr(syscall.Stdout)
+	stdin  = uintptr(syscall.Stdin)
+)
+
+type Kernel struct {
+	SetConsoleCursorPosition,
+	SetConsoleTextAttribute,
+	FillConsoleOutputCharacterW,
+	FillConsoleOutputAttribute,
+	ReadConsoleInputW,
+	GetConsoleScreenBufferInfo,
+	GetConsoleCursorInfo,
+	GetStdHandle CallFunc
+}
+
+type short int16
+type word uint16
+type dword uint32
+type wchar uint16
+
+type _COORD struct {
+	x short
+	y short
+}
+
+func (c *_COORD) ptr() uintptr {
+	return uintptr(*(*int32)(unsafe.Pointer(c)))
+}
+
+const (
+	EVENT_KEY                = 0x0001
+	EVENT_MOUSE              = 0x0002
+	EVENT_WINDOW_BUFFER_SIZE = 0x0004
+	EVENT_MENU               = 0x0008
+	EVENT_FOCUS              = 0x0010
+)
+
+type _KEY_EVENT_RECORD struct {
+	bKeyDown          int32
+	wRepeatCount      word
+	wVirtualKeyCode   word
+	wVirtualScanCode  word
+	unicodeChar       wchar
+	dwControlKeyState dword
+}
+
+// KEY_EVENT_RECORD          KeyEvent;
+// MOUSE_EVENT_RECORD        MouseEvent;
+// WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
+// MENU_EVENT_RECORD         MenuEvent;
+// FOCUS_EVENT_RECORD        FocusEvent;
+type _INPUT_RECORD struct {
+	EventType word
+	Padding   uint16
+	Event     [16]byte
+}
+
+type _CONSOLE_SCREEN_BUFFER_INFO struct {
+	dwSize              _COORD
+	dwCursorPosition    _COORD
+	wAttributes         word
+	srWindow            _SMALL_RECT
+	dwMaximumWindowSize _COORD
+}
+
+type _SMALL_RECT struct {
+	left   short
+	top    short
+	right  short
+	bottom short
+}
+
+type _CONSOLE_CURSOR_INFO struct {
+	dwSize   dword
+	bVisible bool
+}
+
+type CallFunc func(u ...uintptr) error
+
+func NewKernel() *Kernel {
+	k := &Kernel{}
+	kernel32 := syscall.NewLazyDLL("kernel32.dll")
+	v := reflect.ValueOf(k).Elem()
+	t := v.Type()
+	for i := 0; i < t.NumField(); i++ {
+		name := t.Field(i).Name
+		f := kernel32.NewProc(name)
+		v.Field(i).Set(reflect.ValueOf(k.Wrap(f)))
+	}
+	return k
+}
+
+func (k *Kernel) Wrap(p *syscall.LazyProc) CallFunc {
+	return func(args ...uintptr) error {
+		var r0 uintptr
+		var e1 syscall.Errno
+		size := uintptr(len(args))
+		if len(args) <= 3 {
+			buf := make([]uintptr, 3)
+			copy(buf, args)
+			r0, _, e1 = syscall.Syscall(p.Addr(), size,
+				buf[0], buf[1], buf[2])
+		} else {
+			buf := make([]uintptr, 6)
+			copy(buf, args)
+			r0, _, e1 = syscall.Syscall6(p.Addr(), size,
+				buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
+			)
+		}
+
+		if int(r0) == 0 {
+			if e1 != 0 {
+				return error(e1)
+			} else {
+				return syscall.EINVAL
+			}
+		}
+		return nil
+	}
+
+}
+
+func GetConsoleScreenBufferInfo() (*_CONSOLE_SCREEN_BUFFER_INFO, error) {
+	t := new(_CONSOLE_SCREEN_BUFFER_INFO)
+	err := kernel.GetConsoleScreenBufferInfo(
+		stdout,
+		uintptr(unsafe.Pointer(t)),
+	)
+	return t, err
+}
+
+func GetConsoleCursorInfo() (*_CONSOLE_CURSOR_INFO, error) {
+	t := new(_CONSOLE_CURSOR_INFO)
+	err := kernel.GetConsoleCursorInfo(stdout, uintptr(unsafe.Pointer(t)))
+	return t, err
+}
+
+func SetConsoleCursorPosition(c *_COORD) error {
+	return kernel.SetConsoleCursorPosition(stdout, c.ptr())
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services