forked from x/ContextOS
608 lines
15 KiB
Go
608 lines
15 KiB
Go
package cli // {{{
|
|
// }}}
|
|
import ( // {{{
|
|
"bufio"
|
|
"context"
|
|
_ "context/tcp"
|
|
_ "context/web"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
)
|
|
|
|
// }}}
|
|
|
|
type CLI struct {
|
|
out io.WriteCloser
|
|
in io.ReadCloser
|
|
ins []io.ReadCloser
|
|
bio *bufio.Reader
|
|
bios []*bufio.Reader
|
|
|
|
history []map[string]string
|
|
alias map[string]string
|
|
next string
|
|
exit bool
|
|
|
|
target *ctx.Context
|
|
*ctx.Context
|
|
}
|
|
|
|
func (cli *CLI) push(f io.ReadCloser) { // {{{
|
|
if cli.ins == nil || cli.bios == nil {
|
|
cli.ins = make([]io.ReadCloser, 0, 3)
|
|
cli.bios = make([]*bufio.Reader, 0, 3)
|
|
}
|
|
|
|
cli.in = f
|
|
cli.ins = append(cli.ins, cli.in)
|
|
cli.bio = bufio.NewReader(f)
|
|
cli.bios = append(cli.bios, cli.bio)
|
|
}
|
|
|
|
// }}}
|
|
func (cli *CLI) parse() bool { // {{{
|
|
if len(cli.ins) == 1 || cli.Conf("slient") != "yes" {
|
|
cli.echo(cli.Conf("PS1"))
|
|
}
|
|
|
|
line := ""
|
|
if cli.next == "" {
|
|
ls, e := cli.bio.ReadString('\n')
|
|
if e == io.EOF {
|
|
l := len(cli.ins)
|
|
if l == 1 {
|
|
cli.echo("\n%s\n", cli.Conf("结束语"))
|
|
return false
|
|
ls = "exit"
|
|
e = nil
|
|
} else {
|
|
cli.ins = cli.ins[:l-1]
|
|
cli.bios = cli.bios[:l-1]
|
|
cli.in = cli.ins[l-2]
|
|
cli.bio = cli.bios[l-2]
|
|
return true
|
|
}
|
|
}
|
|
cli.Assert(e)
|
|
line = ls
|
|
|
|
if len(cli.ins) > 1 || cli.Conf("slient") != "yes" {
|
|
cli.echo(line)
|
|
}
|
|
|
|
if len(line) == 1 {
|
|
if len(cli.ins) == 1 {
|
|
line = cli.history[len(cli.history)-1]["cli"]
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
} else {
|
|
line = cli.next
|
|
cli.next = ""
|
|
|
|
if cli.Conf("slient") != "yes" {
|
|
cli.echo(line)
|
|
cli.echo("\n")
|
|
}
|
|
}
|
|
|
|
back:
|
|
line = strings.TrimSpace(line)
|
|
if line[0] == '#' {
|
|
return true
|
|
}
|
|
ls := strings.Split(line, " ")
|
|
|
|
msg := &ctx.Message{Wait: make(chan bool)}
|
|
msg.Message = cli.Resource[0]
|
|
msg.Context = cli.Context
|
|
msg.Target = cli.target
|
|
|
|
r := rune(ls[0][0])
|
|
if !unicode.IsNumber(r) || !unicode.IsLetter(r) || r == '$' || r == '_' {
|
|
if _, ok := cli.alias[string(r)]; ok {
|
|
msg.Add("detail", ls[0][:1])
|
|
if len(ls[0]) > 1 {
|
|
ls[0] = ls[0][1:]
|
|
} else {
|
|
if len(ls) > 1 {
|
|
ls = ls[1:]
|
|
} else {
|
|
ls = nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(ls); i++ {
|
|
ls[i] = strings.TrimSpace(ls[i])
|
|
|
|
if ls[i][0] == '#' {
|
|
break
|
|
}
|
|
if ls[i] != "" {
|
|
msg.Add("detail", ls[i])
|
|
}
|
|
}
|
|
|
|
ls = msg.Meta["detail"]
|
|
if n, e := strconv.Atoi(ls[0]); e == nil && 0 <= n && n < len(cli.history) && ls[0] != cli.history[n]["cli"] {
|
|
line = cli.history[n]["cli"]
|
|
msg.Meta["detail"] = nil
|
|
goto back
|
|
}
|
|
|
|
msg.Post(cli.Context)
|
|
|
|
for _, v := range msg.Meta["result"] {
|
|
cli.echo(v)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// }}}
|
|
func (cli *CLI) echo(str string, arg ...interface{}) { // {{{
|
|
if len(cli.ins) == 1 || cli.Conf("slient") != "yes" {
|
|
fmt.Fprintf(cli.out, str, arg...)
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
|
|
func (cli *CLI) Begin(m *ctx.Message) ctx.Server { // {{{
|
|
cli.history = make([]map[string]string, 0, 100)
|
|
cli.target = cli.Context
|
|
return cli.Server
|
|
}
|
|
|
|
// }}}
|
|
func (cli *CLI) Start(m *ctx.Message) bool { // {{{
|
|
cli.Capi("nterm", 1)
|
|
defer cli.Capi("nterm", -1)
|
|
|
|
if stream, ok := m.Data["io"]; ok {
|
|
io := stream.(io.ReadWriteCloser)
|
|
cli.out = io
|
|
cli.push(io)
|
|
|
|
cli.echo("%s\n", cli.Conf("开场白"))
|
|
|
|
if f, e := os.Open(cli.Conf("init.sh")); e == nil {
|
|
cli.push(f)
|
|
}
|
|
|
|
defer recover()
|
|
go cli.AssertOne(m, func(c *ctx.Context, m *ctx.Message) {
|
|
for cli.parse() {
|
|
}
|
|
})
|
|
}
|
|
|
|
for cli.Deal(func(msg *ctx.Message) bool {
|
|
arg := msg.Meta["detail"]
|
|
if a, ok := cli.alias[arg[0]]; ok {
|
|
arg[0] = a
|
|
}
|
|
return true
|
|
|
|
}, func(msg *ctx.Message) bool {
|
|
arg := msg.Meta["detail"]
|
|
cli.history = append(cli.history, map[string]string{
|
|
"time": time.Now().Format("15:04:05"),
|
|
"index": fmt.Sprintf("%d", len(cli.history)),
|
|
"cli": strings.Join(arg, " "),
|
|
})
|
|
|
|
if cli.exit == true {
|
|
return false
|
|
}
|
|
return true
|
|
|
|
}) {
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// }}}
|
|
func (cli *CLI) Spawn(c *ctx.Context, m *ctx.Message, arg ...string) ctx.Server { // {{{
|
|
c.Caches = map[string]*ctx.Cache{
|
|
"status": &ctx.Cache{Name: "status", Value: "stop", Help: "服务状态"},
|
|
}
|
|
c.Configs = map[string]*ctx.Config{
|
|
"address": &ctx.Config{Name: "address", Value: arg[0], Help: "监听地址"},
|
|
"protocol": &ctx.Config{Name: "protocol", Value: arg[1], Help: "监听协议"},
|
|
"init.sh": &ctx.Config{Name: "init.sh", Value: "", Help: "默认启动脚本"},
|
|
}
|
|
c.Commands = cli.Commands
|
|
c.Messages = make(chan *ctx.Message, 10)
|
|
|
|
s := new(CLI)
|
|
s.Context = c
|
|
s.alias = map[string]string{
|
|
"~": "context",
|
|
"!": "history",
|
|
"@": "config",
|
|
"$": "cache",
|
|
"&": "server",
|
|
"*": "message",
|
|
}
|
|
return s
|
|
}
|
|
|
|
// }}}
|
|
func (cli *CLI) Exit(m *ctx.Message, arg ...string) bool { // {{{
|
|
if cli.Context != Index {
|
|
delete(cli.Context.Context.Contexts, cli.Name)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// }}}
|
|
|
|
var Index = &ctx.Context{Name: "cli", Help: "管理终端",
|
|
Caches: map[string]*ctx.Cache{
|
|
"nterm": &ctx.Cache{Name: "nterm", Value: "0", Help: "终端数量"},
|
|
"status": &ctx.Cache{Name: "status", Value: "stop", Help: "服务状态"},
|
|
"nhistory": &ctx.Cache{Name: "nhistory", Value: "0", Help: "终端数量", Hand: func(c *ctx.Context, x *ctx.Cache, arg ...string) string {
|
|
if cli, ok := c.Server.(*CLI); ok { // {{{
|
|
return fmt.Sprintf("%d", len(cli.history))
|
|
}
|
|
|
|
return x.Value
|
|
// }}}
|
|
}},
|
|
},
|
|
Configs: map[string]*ctx.Config{
|
|
"开场白": &ctx.Config{Name: "开场白", Value: "\n~~~ Hello Context & Message World ~~~\n", Help: "开场白"},
|
|
"结束语": &ctx.Config{Name: "结束语", Value: "\n~~~ Byebye Context & Message World ~~~\n", Help: "结束语"},
|
|
"slient": &ctx.Config{Name: "slient", Value: "yes", Help: "屏蔽脚本输出"},
|
|
|
|
"PS1": &ctx.Config{Name: "PS1", Value: "target", Help: "命令行提示符", Hand: func(c *ctx.Context, x *ctx.Config, arg ...string) string {
|
|
cli, ok := c.Server.(*CLI) // {{{
|
|
if ok && cli.target != nil {
|
|
// c = cli.target
|
|
switch x.Value {
|
|
case "target":
|
|
return fmt.Sprintf("%s[%s]\033[32m%s\033[0m> ", c.Cap("nhistory"), time.Now().Format("15:04:05"), cli.target.Name)
|
|
case "detail":
|
|
return fmt.Sprintf("%s[%s](%s,%s,%s)\033[32m%s\033[0m> ", c.Cap("nhistory"), time.Now().Format("15:04:05"), c.Cap("ncontext"), c.Cap("nmessage"), c.Cap("nserver"), c.Name)
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("[%s]\033[32m%s\033[0m ", time.Now().Format("15:04:05"), x.Value)
|
|
// }}}
|
|
}},
|
|
},
|
|
Commands: map[string]*ctx.Command{
|
|
"context": &ctx.Command{Name: "context [spawn|find|search name [which]]|root|back|home", Help: "查看上下文", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
cli, ok := c.Server.(*CLI) // {{{
|
|
switch len(arg) {
|
|
case 0:
|
|
// cs := []*ctx.Context{m.Target}
|
|
cs := []*ctx.Context{m.Target.Root}
|
|
for i := 0; i < len(cs); i++ {
|
|
if len(cs[i].Contexts) > 0 {
|
|
m.Echo("%s: ", cs[i].Name)
|
|
for k, v := range cs[i].Contexts {
|
|
cs = append(cs, v)
|
|
m.Echo("%s, ", k)
|
|
}
|
|
m.Echo("\n")
|
|
}
|
|
}
|
|
case 1:
|
|
switch arg[0] {
|
|
case "root":
|
|
if ok {
|
|
cli.target = cli.Context.Root
|
|
} else {
|
|
m.Target = m.Target.Root
|
|
}
|
|
case "back":
|
|
if ok {
|
|
if cli.Context.Context != nil {
|
|
cli.target = cli.Context.Context
|
|
}
|
|
} else {
|
|
if m.Target.Context != nil {
|
|
m.Target = m.Target.Context
|
|
}
|
|
}
|
|
case "home":
|
|
if ok {
|
|
cli.target = cli.Context
|
|
} else {
|
|
m.Target = m.Context
|
|
}
|
|
default:
|
|
// if cs := m.Target.Find(strings.Split(arg[1], ".")); cs != nil {
|
|
if cs := c.Root.Search(arg[0]); cs != nil && len(cs) > 0 {
|
|
if ok {
|
|
cli.target = cs[0]
|
|
} else {
|
|
m.Target = cs[0]
|
|
}
|
|
}
|
|
}
|
|
case 2, 3:
|
|
switch arg[0] {
|
|
case "spawn":
|
|
m.Target.Spawn(m, arg[1])
|
|
case "find":
|
|
cs := m.Target.Find(arg[1])
|
|
if cs != nil {
|
|
m.Echo("%s: %s\n", cs.Name, cs.Help)
|
|
if len(arg) == 4 {
|
|
if ok {
|
|
cli.target = cs
|
|
} else {
|
|
m.Target = cs
|
|
}
|
|
}
|
|
}
|
|
case "search":
|
|
cs := m.Target.Search(arg[1])
|
|
for i, v := range cs {
|
|
m.Echo("[%d] %s: %s\n", i, v.Name, v.Help)
|
|
}
|
|
|
|
if len(arg) == 3 {
|
|
n, e := strconv.Atoi(arg[2])
|
|
if 0 <= n && n < len(cs) && e == nil {
|
|
if ok {
|
|
cli.target = cs[n]
|
|
} else {
|
|
m.Target = cs[n]
|
|
}
|
|
} else {
|
|
m.Echo("参数错误(0<=n<%s)", len(cs))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"message": &ctx.Command{Name: "message detail...", Help: "查看上下文", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
// {{{
|
|
ms := []*ctx.Message{ctx.Pulse}
|
|
for i := 0; i < len(ms); i++ {
|
|
m.Echo("%d %s.%s -> %s.%d\n", ms[i].Code, ms[i].Context.Name, ms[i].Name, ms[i].Target.Name, ms[i].Index)
|
|
// m.Echo("%d %s %s.%s -> %s.%d\n", ms[i].Code, ms[i].Time.Format("2006/01/02 15:03:04"), ms[i].Context.Name, ms[i].Name, ms[i].Target.Name, ms[i].Index)
|
|
ms = append(ms, ms[i].Messages...)
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"server": &ctx.Command{Name: "server start|stop|switch", Help: "服务启动停止切换", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
s := m.Target // {{{
|
|
switch len(arg) {
|
|
case 0:
|
|
cs := []*ctx.Context{m.Target.Root}
|
|
for i := 0; i < len(cs); i++ {
|
|
if x, ok := cs[i].Caches["status"]; ok {
|
|
m.Echo("%s(%s): %s\n", cs[i].Name, x.Value, cs[i].Help)
|
|
}
|
|
|
|
for _, v := range cs[i].Contexts {
|
|
cs = append(cs, v)
|
|
}
|
|
}
|
|
return "server start"
|
|
case 1:
|
|
switch arg[0] {
|
|
case "start":
|
|
if s != nil {
|
|
go s.Start(m)
|
|
}
|
|
case "stop":
|
|
case "switch":
|
|
}
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"source": &ctx.Command{Name: "source file", Help: "运行脚本", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
cli := c.Server.(*CLI) // {{{
|
|
switch len(arg) {
|
|
case 1:
|
|
f, e := os.Open(arg[0])
|
|
c.Assert(e)
|
|
cli.push(f)
|
|
}
|
|
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"alias": &ctx.Command{Name: "alias [short [long]]", Help: "查看日志", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
cli := c.Server.(*CLI) // {{{
|
|
switch len(arg) {
|
|
case 0:
|
|
for k, v := range cli.alias {
|
|
m.Echo("%s: %s\n", k, v)
|
|
}
|
|
case 2:
|
|
switch arg[0] {
|
|
case "delete":
|
|
delete(cli.alias, arg[1])
|
|
default:
|
|
cli.alias[arg[0]] = arg[1]
|
|
m.Echo("%s: %s\n", arg[0], cli.alias[arg[0]])
|
|
}
|
|
default:
|
|
cli.alias[arg[0]] = strings.Join(arg[1:], " ")
|
|
m.Echo("%s: %s\n", arg[0], cli.alias[arg[0]])
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"history": &ctx.Command{Name: "history number", Help: "查看日志", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
cli := c.Server.(*CLI) // {{{
|
|
switch len(arg) {
|
|
case 0:
|
|
for i, v := range cli.history {
|
|
m.Echo("%d %s %s\n", i, v["time"], v["cli"])
|
|
}
|
|
case 1:
|
|
n, e := strconv.Atoi(arg[0])
|
|
if e == nil && 0 <= n && n < len(cli.history) {
|
|
log.Println("shy log why:", cli.history[n]["cli"])
|
|
cli.next = cli.history[n]["cli"]
|
|
}
|
|
default:
|
|
n, e := strconv.Atoi(arg[0])
|
|
if e == nil && 0 <= n && n < len(cli.history) {
|
|
cli.history[n]["cli"] = strings.Join(arg[1:], " ")
|
|
}
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"command": &ctx.Command{Name: "command [name [value [help]]]", Help: "查看修改添加配置", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
switch len(arg) { // {{{
|
|
case 0:
|
|
for k, v := range m.Target.Commands {
|
|
m.Echo("%s: %s\n", k, v.Help)
|
|
}
|
|
case 2:
|
|
if v, ok := m.Target.Commands[arg[0]]; ok {
|
|
m.Echo("%s: %s\n", v.Name, v.Help)
|
|
}
|
|
case 3:
|
|
switch arg[0] {
|
|
case "delete":
|
|
if _, ok := m.Target.Commands[arg[1]]; ok {
|
|
delete(m.Target.Commands, arg[1])
|
|
}
|
|
}
|
|
}
|
|
|
|
m.Echo("\n")
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"config": &ctx.Command{Name: "config [name [value [help]]]", Help: "查看修改添加配置", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
switch len(arg) { // {{{
|
|
case 0:
|
|
for k, v := range m.Target.Configs {
|
|
m.Echo("%s(%s): %s\n", k, v.Value, v.Help)
|
|
}
|
|
case 1:
|
|
if v, ok := m.Target.Configs[arg[0]]; ok {
|
|
m.Echo("%s: %s\n", v.Name, v.Help)
|
|
}
|
|
case 2:
|
|
switch arg[0] {
|
|
case "void":
|
|
m.Target.Conf(arg[1], "")
|
|
case "delete":
|
|
if _, ok := m.Target.Configs[arg[1]]; ok {
|
|
delete(m.Target.Configs, arg[1])
|
|
}
|
|
default:
|
|
m.Target.Conf(arg[0], arg[1])
|
|
}
|
|
case 4:
|
|
m.Target.Conf(arg[0], arg[1:]...)
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"cache": &ctx.Command{Name: "cache [name [value [help]]]", Help: "查看修改添加配置", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
switch len(arg) { // {{{
|
|
case 0:
|
|
for k, v := range m.Target.Caches {
|
|
m.Echo("%s(%s): %s\n", k, v.Value, v.Help)
|
|
}
|
|
case 1:
|
|
if v, ok := m.Target.Caches[arg[0]]; ok {
|
|
m.Echo("%s: %s\n", v.Name, v.Help)
|
|
}
|
|
case 2:
|
|
switch arg[0] {
|
|
case "delete":
|
|
if _, ok := m.Target.Caches[arg[1]]; ok {
|
|
delete(m.Target.Caches, arg[1])
|
|
}
|
|
default:
|
|
if _, ok := m.Target.Caches[arg[0]]; ok {
|
|
m.Echo("%s: %s\n", arg[0], m.Target.Cap(arg[0], arg[1:]...))
|
|
}
|
|
}
|
|
case 4:
|
|
m.Target.Cap(arg[0], arg[1:]...)
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"exit": &ctx.Command{Name: "exit", Help: "退出", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
cli, ok := m.Target.Server.(*CLI) // {{{
|
|
if !ok {
|
|
cli, ok = m.Context.Server.(*CLI)
|
|
}
|
|
if ok {
|
|
if !cli.exit {
|
|
m.Echo(c.Conf("结束语"))
|
|
cli.Context.Exit(m)
|
|
}
|
|
cli.exit = true
|
|
}
|
|
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"remote": &ctx.Command{Name: "remote master|slave listen|dial address protocol", Help: "建立远程连接", Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
switch len(arg) { // {{{
|
|
case 0:
|
|
case 4:
|
|
if arg[0] == "master" {
|
|
if arg[1] == "dial" {
|
|
} else {
|
|
}
|
|
} else {
|
|
if arg[1] == "listen" {
|
|
s := c.Root.Find(arg[3])
|
|
m.Message.Spawn(s, arg[2]).Cmd("listen", arg[2])
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"accept": &ctx.Command{Name: "accept address protocl", Help: "建立远程连接",
|
|
Options: map[string]string{"io": "读写流"},
|
|
Hand: func(c *ctx.Context, m *ctx.Message, key string, arg ...string) string {
|
|
m.Start(fmt.Sprintf("PTS%d", c.Capi("nterm")), arg[1]) // {{{
|
|
return ""
|
|
// }}}
|
|
}},
|
|
"void": &ctx.Command{Name: "", Help: "", Hand: nil},
|
|
},
|
|
Messages: make(chan *ctx.Message, 10),
|
|
}
|
|
|
|
func init() {
|
|
cli := &CLI{alias: map[string]string{
|
|
"~": "context",
|
|
"!": "history",
|
|
"@": "config",
|
|
"$": "cache",
|
|
"&": "server",
|
|
"*": "message",
|
|
}}
|
|
cli.Context = Index
|
|
ctx.Index.Register(Index, cli)
|
|
}
|