package ssh import ( "bufio" "bytes" "fmt" "io" "os" "path" "strings" "time" ice "shylinux.com/x/icebergs" "shylinux.com/x/icebergs/base/aaa" "shylinux.com/x/icebergs/base/ctx" "shylinux.com/x/icebergs/base/mdb" "shylinux.com/x/icebergs/base/nfs" kit "shylinux.com/x/toolkits" ) func Render(msg *ice.Message, cmd string, args ...interface{}) (res string) { switch arg := kit.Simple(args...); cmd { case ice.RENDER_VOID: return res case ice.RENDER_RESULT: if len(arg) > 0 { msg.Resultv(arg) } res = msg.Result() default: if res = msg.Result(); res == "" { res = msg.Table().Result() } } if fmt.Fprint(msg.O, res); !strings.HasSuffix(res, ice.NL) { fmt.Fprint(msg.O, ice.NL) } return res } type Frame struct { source string target *ice.Context stdout io.Writer stdin io.Reader pipe io.Writer ps1 []string ps2 []string res string count int } func (f *Frame) prompt(m *ice.Message, list ...string) *Frame { if f.source != STDIO { return f } if len(list) == 0 { list = append(list, f.ps1...) } fmt.Fprintf(f.stdout, "\r") for _, v := range list { switch v { case kit.MDB_COUNT: fmt.Fprintf(f.stdout, "%d", f.count) case kit.MDB_TIME: fmt.Fprintf(f.stdout, time.Now().Format("15:04:05")) case TARGET: fmt.Fprintf(f.stdout, f.target.Name) default: fmt.Fprintf(f.stdout, v) } } return f } func (f *Frame) printf(m *ice.Message, res string, arg ...interface{}) *Frame { if len(arg) > 0 { fmt.Fprintf(f.stdout, res, arg...) } else { fmt.Fprint(f.stdout, res) } return f } func (f *Frame) change(m *ice.Message, ls []string) []string { if len(ls) == 1 && ls[0] == "~" { // 模块列表 ls = []string{ctx.CONTEXT} } else if len(ls) > 0 && strings.HasPrefix(ls[0], "~") { // 切换模块 target := ls[0][1:] if ls = ls[1:]; len(target) == 0 && len(ls) > 0 { target, ls = ls[0], ls[1:] } if target == "~" { target = "" } m.Spawn(f.target).Search(target+".", func(p *ice.Context, s *ice.Context, key string) { m.Info("choice: %s", s.Name) f.target = s }) } return ls } func (f *Frame) alias(m *ice.Message, ls []string) []string { if alias, ok := m.Optionv(ice.MSG_ALIAS).(map[string]interface{}); ok { if len(ls) > 0 { if a := kit.Simple(alias[ls[0]]); len(a) > 0 { ls = append(append([]string{}, a...), ls[1:]...) } } } return ls } func (f *Frame) parse(m *ice.Message, line string) string { if strings.HasPrefix(line, "<") { fmt.Fprintf(m.O, line) return "" } for _, one := range kit.Split(line, ";", ";", ";") { one = strings.TrimSpace(one) msg := m.Spawn(f.target) msg.Option("_cmd", one) ls := f.change(msg, f.alias(msg, kit.Split(one))) if len(ls) == 0 { continue } msg.Cmdy(ls[0], ls[1:]) _args, _ := msg.Optionv(ice.MSG_ARGS).([]interface{}) f.res = Render(msg, msg.Option(ice.MSG_OUTPUT), _args...) } return "" } func (f *Frame) scan(m *ice.Message, h, line string) *Frame { f.ps1 = kit.Simple(m.Confv(PROMPT, kit.Keym(PS1))) f.ps2 = kit.Simple(m.Confv(PROMPT, kit.Keym(PS2))) ps := f.ps1 m.Sleep("300ms") m.I, m.O = f.stdin, f.stdout bio := bufio.NewScanner(f.stdin) for f.prompt(m, ps...); bio.Scan() && f.stdin != nil; f.prompt(m, ps...) { if h == STDIO && len(bio.Text()) == 0 { continue // 空行 } m.Cmdx(mdb.INSERT, SOURCE, kit.Keys(kit.MDB_HASH, h), mdb.LIST, kit.MDB_TEXT, bio.Text()) f.count++ if len(bio.Text()) == 0 { if strings.Count(line, "`")%2 == 1 { line += ice.NL } continue // 空行 } if strings.HasSuffix(bio.Text(), "\\") { line += bio.Text()[:len(bio.Text())-1] ps = f.ps2 continue // 续行 } if line += bio.Text(); strings.Count(line, "`")%2 == 1 { line += ice.NL ps = f.ps2 continue // 多行 } if line = kit.Split(line, "#", "#", "#")[0]; len(line) == 0 { continue // 注释 } if ps = f.ps1; f.stdout == os.Stdout { f.printf(m, "\033[0m") // 清空格式 } line = f.parse(m, line) } return f } func (f *Frame) close() { if stdin, ok := f.stdin.(io.Closer); ok { stdin.Close() } f.stdin = nil } func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server { return f } func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server { return &Frame{} } func (f *Frame) Start(m *ice.Message, arg ...string) bool { m.Optionv(FRAME, f) switch f.source = kit.Select(STDIO, arg, 0); f.source { case STDIO: // 终端交互 m.Cap(ice.CTX_STREAM, f.source) f.target = m.Target() r, w, _ := os.Pipe() m.Go(func() { io.Copy(w, os.Stdin) }) f.stdin, f.stdout = r, os.Stdout f.pipe = w aaa.UserRoot(m) m.Option(ice.MSG_OPTS, ice.MSG_USERNAME) m.Conf(SOURCE, kit.Keys(kit.MDB_HASH, STDIO, kit.Keym(kit.MDB_NAME)), STDIO) m.Conf(SOURCE, kit.Keys(kit.MDB_HASH, STDIO, kit.Keym(kit.MDB_TIME)), m.Time()) f.count = kit.Int(m.Conf(SOURCE, kit.Keys(kit.MDB_HASH, STDIO, kit.Keym(kit.MDB_COUNT)))) + 1 f.scan(m, STDIO, "") default: // 脚本文件 if strings.Contains(m.Option(ice.MSG_SCRIPT), "/") { f.source = path.Join(path.Dir(m.Option(ice.MSG_SCRIPT)), f.source) } m.Option(ice.MSG_SCRIPT, f.source) f.target = m.Source() if msg := m.Cmd(nfs.CAT, f.source); msg.Result(0) == ice.ErrWarn { return true // 查找失败 } else { buf := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS)) defer func() { m.Echo(buf.String()) }() f.stdin, f.stdout = bytes.NewBuffer([]byte(msg.Result())), buf } f.count = 1 f.scan(m, m.Cmdx(mdb.INSERT, SOURCE, "", mdb.HASH, kit.MDB_NAME, f.source), "") } return true } func (f *Frame) Close(m *ice.Message, arg ...string) bool { return true } const ( FRAME = "frame" STDIO = "stdio" PS1 = "PS1" PS2 = "PS2" ) const ( SCRIPT = "script" SOURCE = "source" TARGET = "target" PROMPT = "prompt" PRINTF = "printf" SCREEN = "screen" RETURN = "return" ) func init() { Index.Merge(&ice.Context{Configs: map[string]*ice.Config{ SOURCE: {Name: SOURCE, Help: "加载脚本", Value: kit.Data()}, PROMPT: {Name: PROMPT, Help: "命令提示", Value: kit.Data( PS1, []interface{}{"\033[33;44m", kit.MDB_COUNT, "[", kit.MDB_TIME, "]", "\033[5m", TARGET, "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"}, PS2, []interface{}{kit.MDB_COUNT, " ", TARGET, "> "}, )}, }, Commands: map[string]*ice.Command{ ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { if f, ok := m.Target().Server().(*Frame); ok { f.close() } }}, SOURCE: {Name: "source file", Help: "脚本解析", Action: ice.MergeAction(map[string]*ice.Action{ mdb.REPEAT: {Name: "repeat", Help: "执行", Hand: func(m *ice.Message, arg ...string) { m.Cmdy(SCREEN, m.Option(kit.MDB_TEXT)) m.ProcessInner() }}, }, mdb.ZoneAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { if len(arg) > 0 && kit.Ext(arg[0]) == ice.SHY { (&Frame{}).Start(m, arg...) return // 脚本解析 } }}, TARGET: {Name: "target name run:button", Help: "当前模块", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { f := m.Optionv(FRAME).(*Frame) m.Search(arg[0]+ice.PT, func(p *ice.Context, s *ice.Context, key string) { f.target = s }) f.prompt(m) }}, PROMPT: {Name: "prompt arg run:button", Help: "命令提示", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { f := m.Optionv(FRAME).(*Frame) f.ps1 = arg f.prompt(m) }}, PRINTF: {Name: "printf run:button text:textarea", Help: "输出显示", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { f := m.Optionv(FRAME).(*Frame) f.printf(m, arg[0]) }}, SCREEN: {Name: "screen run:button text:textarea", Help: "输出命令", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { f := m.Optionv(FRAME).(*Frame) for _, line := range kit.Split(arg[0], ice.NL, ice.NL) { fmt.Fprintf(f.pipe, line+ice.NL) f.printf(m, line+ice.NL) m.Sleep("300ms") } m.Echo(f.res) }}, RETURN: {Name: "return", Help: "结束脚本", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { f := m.Optionv(FRAME).(*Frame) f.close() }}, }}) }