1
0
mirror of https://shylinux.com/x/ContextOS synced 2025-04-25 16:58:06 +08:00
2018-08-20 21:54:14 +08:00

1399 lines
38 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package nfs // {{{
// }}}
import ( // {{{
"contexts"
"encoding/json"
"github.com/nsf/termbox-go"
"github.com/skip2/go-qrcode"
"net"
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"os/exec"
"path"
"runtime"
"strconv"
"strings"
"unicode"
)
// }}}
var FileNotExist = errors.New("file not exist")
type NFS struct {
in *os.File
out *os.File
history []string
pages []string
width int
height int
paths []string
io net.Conn
send chan *ctx.Message
recv chan *ctx.Message
hand map[int]*ctx.Message
*bufio.Reader
*bufio.Writer
target *ctx.Context
cli *ctx.Message
*ctx.Message
*ctx.Context
}
func (nfs *NFS) open(name string) (*os.File, error) { // {{{
if path.IsAbs(name) {
nfs.Message.Log("info", "open %s", name)
return os.Open(name)
}
for i := len(nfs.paths) - 1; i >= 0; i-- {
if f, e := os.Open(path.Join(nfs.paths[i], name)); e == nil {
nfs.Log("info", "open %s", path.Join(nfs.paths[i], name))
return f, e
}
}
nfs.Log("info", "open %s", name)
return os.Open(name)
}
// }}}
func dir(m *ctx.Message, name string, level int, deep bool, fields []string) { // {{{
back, e := os.Getwd()
m.Assert(e)
os.Chdir(name)
defer os.Chdir(back)
s, e := os.Stat(".")
for _, k := range fields {
switch k {
case "filename":
m.Add("append", "filename", "..")
case "dir":
m.Add("append", "dir", "true")
case "size":
m.Add("append", "size", 0)
case "line":
m.Add("append", "line", 0)
case "time":
m.Add("append", "time", s.ModTime().Format("2006-01-02 15:04:05"))
}
}
if fs, e := ioutil.ReadDir("."); m.Assert(e) {
for _, f := range fs {
if f.Name()[0] == '.' {
continue
}
if f.IsDir() {
if m.Has("dirs") {
m.Optioni("dirs", m.Optioni("dirs")+1)
}
} else {
if m.Has("files") {
m.Optioni("files", m.Optioni("files")+1)
}
}
if m.Has("sizes") {
m.Optioni("sizes", m.Optioni("sizes")+int(f.Size()))
}
line := 0
if m.Has("lines") {
if !f.IsDir() {
f, e := os.Open(path.Join(back, name, f.Name()))
m.Assert(e)
defer f.Close()
bio := bufio.NewScanner(f)
for bio.Scan() {
bio.Text()
line++
}
m.Optioni("lines", m.Optioni("lines")+line)
}
}
filename := ""
switch m.Confx("dir_name") {
case "name":
filename = f.Name()
case "tree":
filename = strings.Repeat(" ", level) + f.Name()
default:
filename = path.Join(back, name, f.Name())
}
if !(m.Confx("dir_type") == "file" && f.IsDir() ||
m.Confx("dir_type") == "dir" && !f.IsDir()) {
for _, k := range fields {
switch k {
case "filename":
m.Add("append", "filename", filename)
case "dir":
m.Add("append", "dir", f.IsDir())
case "size":
m.Add("append", "size", f.Size())
case "line":
m.Add("append", "line", line)
case "time":
m.Add("append", "time", f.ModTime().Format("2006-01-02 15:04:05"))
}
}
}
if f.IsDir() && deep {
dir(m, f.Name(), level+1, deep, fields)
}
}
}
}
// }}}
func (nfs *NFS) insert(rest []rune, letters []rune) []rune { // {{{
n := len(rest)
l := len(letters)
rest = append(rest, letters...)
for i := n - 1; i >= 0; i-- {
rest[i+l] = rest[i]
}
for i := 0; i < l; i++ {
rest[i] = letters[i]
}
return rest
}
// }}}
func (nfs *NFS) escape(form string, args ...interface{}) *NFS { // {{{
if !nfs.Caps("windows") {
fmt.Fprintf(nfs.out, "\033[%s", fmt.Sprintf(form, args...))
}
return nfs
}
// }}}
func (nfs *NFS) color(str string, attr ...int) *NFS { // {{{
if !nfs.Confs("color") {
fmt.Fprint(nfs.out, str)
return nfs
}
fg := nfs.Confi("fgcolor")
if len(attr) > 0 {
fg = attr[0]
}
bg := nfs.Confi("bgcolor")
if len(attr) > 1 {
bg = attr[1]
}
for i := 2; i < len(attr); i++ {
nfs.escape("%dm", attr[i])
}
nfs.escape("4%dm", bg).escape("3%dm", fg)
fmt.Fprint(nfs.out, str)
nfs.escape("0m")
return nfs
}
// }}}
func (nfs *NFS) print(str string) bool { // {{{
ls := strings.Split(str, "\n")
for i, l := range ls {
rest := ""
if len(nfs.pages) > 0 && !strings.HasSuffix(nfs.pages[len(nfs.pages)-1], "\n") {
rest = nfs.pages[len(nfs.pages)-1]
nfs.pages = nfs.pages[:len(nfs.pages)-1]
}
if rest += l; i < len(ls)-1 {
rest += "\n"
}
nfs.pages = append(nfs.pages, rest)
if nfs.Capi("cursor_pos") < nfs.height {
nfs.Capi("cursor_pos", 1)
}
}
switch {
case nfs.out != nil:
nfs.color(str)
case nfs.io != nil:
fmt.Fprint(nfs.io, str)
default:
return false
}
return true
}
// }}}
func (nfs *NFS) prompt(arg ...string) string { // {{{
ps := nfs.Option("prompt")
if nfs.Caps("windows") {
nfs.color(ps)
return ps
}
line, rest := "", ""
if len(arg) > 0 {
line = arg[0]
}
if len(arg) > 1 {
rest = arg[1]
}
if !nfs.Caps("windows") && len(nfs.pages) > 0 {
for i := (len(nfs.pages[len(nfs.pages)-1]) - 1) / (nfs.width); i > 0; i-- {
nfs.escape("2K").escape("A")
}
nfs.escape("2K").escape("G").escape("?25h")
}
if len(nfs.pages) > 0 {
nfs.pages = nfs.pages[:len(nfs.pages)-1]
}
nfs.pages = append(nfs.pages, ps+line+rest+"\n")
if nfs.color(ps, nfs.Confi("pscolor")).color(line).color(rest); len(rest) > 0 {
nfs.escape("%dD", len(rest))
}
return ps
}
// }}}
func (nfs *NFS) zone(buf []string, top, height int) (row, col int) { // {{{
row, col = len(buf)-1, 0
for i := nfs.Capi("cursor_pos"); i > top-1; {
if i -= len(buf[row]) / nfs.width; len(buf[row])%nfs.width > 0 {
i--
}
if i < top-1 {
col -= (i - (top - 1)) * nfs.width
} else if i > (top-1) && row > 0 {
row--
}
}
return
}
// }}}
func (nfs *NFS) page(buf []string, row, col, top, height int, status bool) { // {{{
nfs.escape("2J").escape("H")
begin := row
for i := 0; i < height-1; i++ {
if row >= len(buf) {
nfs.color("~\n")
continue
}
if len(buf[row])-col > nfs.width {
nfs.color(buf[row][col : col+nfs.width])
col += nfs.width
continue
}
nfs.color(buf[row][col:])
col = 0
row++
}
if status {
nfs.escape("E").color(fmt.Sprintf("pages: %d/%d", begin, len(nfs.pages)), nfs.Confi("statusfgcolor"), nfs.Confi("statusbgcolor"))
}
}
// }}}
func (nfs *NFS) View(buf []string, top int, height int) { // {{{
row, col := nfs.zone(buf, top, height)
nfs.page(buf, row, col, top, height, true)
for {
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
switch ev.Key {
case termbox.KeyCtrlC:
return
default:
switch ev.Ch {
case 'f':
for i := 0; i < height; i++ {
if len(buf[row][col:]) > nfs.width {
col += nfs.width
} else {
if col = 0; row < len(buf)-nfs.height {
row++
}
}
}
case 'b':
for i := 0; i < height; i++ {
if col -= nfs.width; col < 0 {
if col = 0; row > 0 {
row--
}
}
}
case 'j':
if len(buf[row][col:]) > nfs.width {
col += nfs.width
} else {
if col = 0; row < len(buf)-nfs.height {
row++
}
}
case 'k':
if col -= nfs.width; col < 0 {
if col = 0; row > 0 {
row--
}
}
case 'q':
return
}
nfs.page(buf, row, col, top, height, true)
}
}
}
}
// }}}
func (nfs *NFS) Read(p []byte) (n int, err error) { // {{{
if nfs.Caps("windows") || !nfs.Caps("termbox") {
return nfs.in.Read(p)
}
buf := make([]rune, 0, 1024)
rest := make([]rune, 0, 1024)
back := buf
his := len(nfs.history)
tab := []string{}
tabi := 0
for {
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
switch ev.Key {
case termbox.KeyCtrlC:
termbox.Close()
nfs.out = nil
b := []byte("return\n")
n = len(b)
copy(p, b)
return
os.Exit(1)
case termbox.KeyCtrlV:
nfs.View(nfs.pages, 1, nfs.height)
row, col := nfs.zone(nfs.pages, 1, nfs.height)
nfs.page(nfs.pages, row, col, 1, nfs.Capi("cursor_pos"), false)
case termbox.KeyCtrlL:
nfs.escape("2J").escape("H")
nfs.Cap("cursor_pos", "1")
case termbox.KeyCtrlJ, termbox.KeyCtrlM:
buf = append(buf, rest...)
buf = append(buf, '\n')
nfs.color("\n")
b := []byte(string(buf))
n = len(b)
copy(p, b)
return
case termbox.KeyCtrlP:
for i := 0; i < len(nfs.history); i++ {
his = (his + len(nfs.history) - 1) % len(nfs.history)
if strings.HasPrefix(nfs.history[his], string(buf)) {
rest = rest[:0]
rest = append(rest, []rune(nfs.history[his][len(buf):])...)
break
}
}
case termbox.KeyCtrlN:
for i := 0; i < len(nfs.history); i++ {
his = (his + len(nfs.history) + 1) % len(nfs.history)
if strings.HasPrefix(nfs.history[his], string(buf)) {
rest = rest[:0]
rest = append(rest, []rune(nfs.history[his][len(buf):])...)
break
}
}
case termbox.KeyCtrlA:
if len(buf) == 0 {
continue
}
rest = nfs.insert(rest, buf)
buf = buf[:0]
case termbox.KeyCtrlE:
if len(rest) == 0 {
continue
}
buf = append(buf, rest...)
rest = rest[:0]
case termbox.KeyCtrlB:
if len(buf) == 0 {
continue
}
rest = nfs.insert(rest, []rune{buf[len(buf)-1]})
buf = buf[:len(buf)-1]
case termbox.KeyCtrlF:
if len(rest) == 0 {
continue
}
buf = append(buf, rest[0])
rest = rest[1:]
case termbox.KeyCtrlW:
if len(buf) > 0 {
c := buf[len(buf)-1]
for len(buf) > 0 && unicode.IsSpace(c) && unicode.IsSpace(buf[len(buf)-1]) {
buf = buf[:len(buf)-1]
}
for len(buf) > 0 && unicode.IsPunct(c) && unicode.IsPunct(buf[len(buf)-1]) {
buf = buf[:len(buf)-1]
}
for len(buf) > 0 && unicode.IsLetter(c) && unicode.IsLetter(buf[len(buf)-1]) {
buf = buf[:len(buf)-1]
}
for len(buf) > 0 && unicode.IsDigit(c) && unicode.IsDigit(buf[len(buf)-1]) {
buf = buf[:len(buf)-1]
}
}
case termbox.KeyCtrlH:
if len(buf) == 0 {
continue
}
buf = buf[:len(buf)-1]
case termbox.KeyCtrlD:
if len(rest) == 0 {
continue
}
rest = rest[1:]
case termbox.KeyCtrlU:
if len(buf) > 0 {
back = back[:0]
back = append(back, buf...)
}
tab = tab[:0]
buf = buf[:0]
case termbox.KeyCtrlK:
if len(rest) > 0 {
back = append([]rune{}, rest...)
}
rest = rest[:0]
case termbox.KeyCtrlY:
buf = append(buf, back...)
case termbox.KeyCtrlT:
if l := len(buf); l > 1 {
buf[l-1], buf[l-2] = buf[l-2], buf[l-1]
}
case termbox.KeyCtrlI:
if len(tab) == 0 {
tabi = 0
prefix := string(buf)
nfs.Message.BackTrace(func(m *ctx.Message) bool {
for k, _ := range m.Target().Commands {
if strings.HasPrefix(k, prefix) {
tab = append(tab, k[len(prefix):])
}
}
return true
}, nfs.Optionv("ps_target").(*ctx.Context))
}
if tabi >= 0 && tabi < len(tab) {
rest = append(rest[:0], []rune(tab[tabi])...)
tabi = (tabi + 1) % len(tab)
}
case termbox.KeySpace:
tab = tab[:0]
buf = append(buf, ' ')
if len(rest) == 0 {
nfs.color(" ")
continue
}
default:
tab = tab[:0]
buf = append(buf, ev.Ch)
if len(rest) == 0 {
nfs.color(string(ev.Ch))
}
}
nfs.prompt(string(buf), string(rest))
}
}
return
}
// }}}
func (nfs *NFS) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { // {{{
if len(arg) > 0 && (arg[0] == "scan" || arg[0] == "open" || arg[0] == "append") {
c.Caches = map[string]*ctx.Cache{
"pos": &ctx.Cache{Name: "pos", Value: "0", Help: "pos"},
"size": &ctx.Cache{Name: "size", Value: "0", Help: "size"},
"nread": &ctx.Cache{Name: "nread", Value: "0", Help: "nread"},
"nwrite": &ctx.Cache{Name: "nwrite", Value: "0", Help: "nwrite"},
"nline": &ctx.Cache{Name: "缓存命令行数", Value: "0", Help: "缓存命令行数"},
}
c.Configs = map[string]*ctx.Config{}
} else {
c.Caches = map[string]*ctx.Cache{
"nsend": &ctx.Cache{Name: "消息发送数量", Value: "0", Help: "消息发送数量"},
"nrecv": &ctx.Cache{Name: "消息接收数量", Value: "0", Help: "消息接收数量"},
"nread": &ctx.Cache{Name: "nread", Value: "0", Help: "nread"},
"nwrite": &ctx.Cache{Name: "nwrite", Value: "0", Help: "nwrite"},
}
c.Configs = map[string]*ctx.Config{}
}
s := new(NFS)
s.Context = c
return s
}
// }}}
func (nfs *NFS) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{
nfs.Message = m
nfs.width, nfs.height = 1, 1
return nfs
}
// }}}
func (nfs *NFS) Start(m *ctx.Message, arg ...string) bool { // {{{
nfs.Message = m
if len(arg) > 0 && arg[0] == "scan" {
nfs.Caches["windows"] = &ctx.Cache{Name: "windows", Value: "false", Help: "termbox"}
nfs.Caches["termbox"] = &ctx.Cache{Name: "termbox", Value: "false", Help: "termbox"}
nfs.Caches["cursor_pos"] = &ctx.Cache{Name: "cursor_pos", Value: "1", Help: "termbox"}
nfs.Configs["color"] = &ctx.Config{Name: "color", Value: "false", Help: "color"}
nfs.Configs["fgcolor"] = &ctx.Config{Name: "fgcolor", Value: "9", Help: "fgcolor"}
nfs.Configs["bgcolor"] = &ctx.Config{Name: "bgcolor", Value: "9", Help: "bgcolor"}
nfs.Configs["pscolor"] = &ctx.Config{Name: "pscolor", Value: "2", Help: "pscolor"}
nfs.Configs["statusfgcolor"] = &ctx.Config{Name: "statusfgcolor", Value: "1", Help: "pscolor"}
nfs.Configs["statusbgcolor"] = &ctx.Config{Name: "statusbgcolor", Value: "2", Help: "pscolor"}
nfs.in = m.Optionv("in").(*os.File)
bio := bufio.NewScanner(nfs)
s, e := nfs.in.Stat()
m.Assert(e)
m.Capi("size", int(s.Size()))
if m.Cap("stream", arg[1]) == "stdio" {
nfs.out = m.Optionv("out").(*os.File)
if !m.Caps("windows", runtime.GOOS == "windows") {
termbox.Init()
defer termbox.Close()
nfs.width, nfs.height = termbox.Size()
nfs.Cap("termbox", "true")
nfs.Conf("color", "true")
}
if !m.Options("init.shy") {
for _, v := range []string{
// "say you are so pretty",
"context web serve ./ :9094",
} {
m.Back(m.Spawn(m.Source()).Set("detail", v))
}
for _, v := range []string{
"say you are so pretty",
"context web brow 'http://localhost:9094'",
} {
nfs.history = append(nfs.history, v)
m.Capi("nline", 1)
}
for _, v := range []string{
"say you are so pretty\n",
"your can brow 'http://localhost:9094'\n",
"press \"brow\" then press Enter\n",
} {
nfs.print(fmt.Sprintf(v))
}
}
}
line := ""
for nfs.prompt(); !m.Options("scan_end") && bio.Scan(); nfs.prompt() {
text := bio.Text()
m.Capi("nread", len(text)+1)
if line += text; len(text) > 0 && text[len(text)-1] == '\\' {
line = line[:len(line)-1]
continue
}
nfs.history = append(nfs.history, line)
m.Capi("nline", 1)
for i := len(nfs.history) - 1; i < len(nfs.history); i++ {
line = nfs.history[i]
msg := m.Spawn(m.Source()).Set("detail", line)
msg.Option("file_pos", i)
m.Back(msg)
for _, v := range msg.Meta["result"] {
m.Capi("nwrite", len(v))
nfs.print(v)
}
if msg.Append("file_pos0") != "" {
i = msg.Appendi("file_pos0") - 1
}
}
line = ""
}
if !m.Options("scan_end") {
msg := m.Spawn(m.Source()).Set("detail", "return")
m.Back(msg)
}
return true
}
if len(arg) > 0 && (arg[0] == "open" || arg[0] == "append") {
nfs.out = m.Optionv("out").(*os.File)
nfs.in = m.Optionv("in").(*os.File)
s, e := nfs.in.Stat()
m.Assert(e)
m.Capi("size", int(s.Size()))
m.Cap("stream", arg[1])
if arg[0] == "append" {
m.Capi("pos", int(s.Size()))
}
return false
}
m.Cap("stream", m.Option("stream"))
nfs.io = m.Optionv("io").(net.Conn)
nfs.hand = map[int]*ctx.Message{}
nfs.send = make(chan *ctx.Message, 10)
nfs.recv = make(chan *ctx.Message, 10)
go func() { //发送消息队列
for {
select {
case msg := <-nfs.send:
head, body := "detail", "option"
if msg.Hand {
head, body = "result", "append"
send_code := msg.Option("send_code")
msg.Append("send_code1", send_code)
m.Log("info", "%s recv: %v %v", msg.Option("recv_code"), msg.Meta[head], msg.Meta[body])
} else {
msg.Option("send_code", m.Capi("nsend", 1))
m.Log("info", "%d send: %v %v", m.Capi("nsend"), msg.Meta[head], msg.Meta[body])
nfs.hand[m.Capi("nsend")] = msg
}
for _, v := range msg.Meta[head] {
n, e := fmt.Fprintf(nfs.io, "%s: %s\n", head, url.QueryEscape(v))
m.Assert(e)
m.Capi("nwrite", n)
}
for _, k := range msg.Meta[body] {
for _, v := range msg.Meta[k] {
n, e := fmt.Fprintf(nfs.io, "%s: %s\n", url.QueryEscape(k), url.QueryEscape(v))
m.Assert(e)
m.Capi("nwrite", n)
}
}
n, e := fmt.Fprintf(nfs.io, "\n")
m.Assert(e)
m.Capi("nwrite", n)
}
}
}()
go func() { //接收消息队列
bio := bufio.NewScanner(nfs.io)
var e error
var msg *ctx.Message
for head, body := "", ""; bio.Scan(); {
if msg == nil {
msg = m.Sess("target")
}
if msg.Meta == nil {
msg.Meta = map[string][]string{}
}
line := bio.Text()
m.Capi("nread", len(line)+1)
if len(line) == 0 {
if head == "detail" {
m.Log("info", "%d recv: %v %v %v", m.Capi("nrecv", 1), msg.Meta[head], msg.Meta[body], msg.Meta)
msg.Option("recv_code", m.Cap("nrecv"))
nfs.recv <- msg
} else {
m.Log("info", "%d send: %v %v %v", msg.Appendi("send_code1"), msg.Meta[head], msg.Meta[body], msg.Meta)
h := nfs.hand[msg.Appendi("send_code1")]
h.Copy(msg, "result").Copy(msg, "append")
h.Remote <- true
}
msg = nil
continue
}
word := strings.Split(line, ": ")
word[0], e = url.QueryUnescape(word[0])
m.Assert(e)
word[1], e = url.QueryUnescape(word[1])
m.Assert(e)
switch word[0] {
case "detail":
head, body = "detail", "option"
msg.Add(word[0], word[1])
case "result":
head, body = "result", "append"
msg.Add(word[0], word[1])
default:
msg.Add(body, word[0], word[1])
}
}
}()
for {
select {
case msg := <-nfs.recv:
nfs.send <- msg.Cmd()
}
}
return true
}
// }}}
func (nfs *NFS) Close(m *ctx.Message, arg ...string) bool { // {{{
return false
switch nfs.Context {
case m.Target():
if nfs.in != nil {
nfs.in.Close()
nfs.in = nil
}
if nfs.out != nil {
nfs.out.Close()
nfs.out = nil
}
if nfs.io != nil {
nfs.io.Close()
nfs.io = nil
}
case m.Source():
}
return true
}
// }}}
var Index = &ctx.Context{Name: "nfs", Help: "存储中心",
Caches: map[string]*ctx.Cache{
"nfile": &ctx.Cache{Name: "nfile", Value: "-1", Help: "已经打开的文件数量"},
},
Configs: map[string]*ctx.Config{
"pscolor": &ctx.Config{Name: "pscolor", Value: "2", Help: "pscolor"},
"nfs_name": &ctx.Config{Name: "nfs_name", Value: "file", Help: "默认模块命名", Hand: func(m *ctx.Message, x *ctx.Config, arg ...string) string {
if len(arg) > 0 { // {{{
return arg[0]
}
return fmt.Sprintf("%s%d", x.Value, m.Capi("nfile", 1))
// }}}
}},
"nfs_help": &ctx.Config{Name: "nfs_help", Value: "file", Help: "默认模块帮助"},
"buf_size": &ctx.Config{Name: "buf_size", Value: "1024", Help: "读取文件的缓存区的大小"},
"qr_size": &ctx.Config{Name: "qr_size", Value: "256", Help: "生成二维码的图片的大小"},
"dir_name": &ctx.Config{Name: "dir_name(name/tree/path/full)", Value: "name", Help: "dir命令输出文件名的类型, name: 文件名, tree: 带缩进的文件名, path: 相对路径, full: 绝对路径"},
"dir_info": &ctx.Config{Name: "dir_info(sizes/lines/files/dirs)", Value: "sizes lines files dirs", Help: "dir命令输出目录的统计信息, info: 输出统计信息, 否则输出"},
"dir_deep": &ctx.Config{Name: "dir_deep(yes/no)", Value: "yes", Help: "dir命令输出目录的统计信息, info: 输出统计信息, 否则输出"},
"dir_type": &ctx.Config{Name: "dir_type(file/dir)", Value: "file", Help: "dir命令输出的文件类型, file: 只输出普通文件, dir: 只输出目录文件, 否则输出所有文件"},
"dir_field": &ctx.Config{Name: "dir_field", Value: "filename line size time", Help: "表格排序字段"},
"sort_field": &ctx.Config{Name: "sort_field", Value: "line", Help: "表格排序字段"},
"sort_order": &ctx.Config{Name: "sort_order(int/int_r/string/string_r/time/time_r)", Value: "int", Help: "表格排序类型"},
"git_branch": &ctx.Config{Name: "git_branch", Value: "--list", Help: "版本控制状态参数"},
"git_status": &ctx.Config{Name: "git_status", Value: "-sb", Help: "版本控制状态参数"},
"git_diff": &ctx.Config{Name: "git_diff", Value: "--stat", Help: "版本控制状态参数"},
"git_log": &ctx.Config{Name: "git_log", Value: "--pretty=%h %an(%ad) %s --date=format:%m/%d %H:%M --graph", Help: "版本控制状态参数"},
"git_log_form": &ctx.Config{Name: "git_log", Value: "stat", Help: "版本控制状态参数"},
"git_log_skip": &ctx.Config{Name: "git_log", Value: "0", Help: "版本控制状态参数"},
"git_log_line": &ctx.Config{Name: "git_log", Value: "3", Help: "版本控制状态参数"},
"git_path": &ctx.Config{Name: "git_path", Value: ".", Help: "版本控制默认路径"},
"git_info": &ctx.Config{Name: "git_info", Value: "branch status diff log", Help: "命令集合"},
},
Commands: map[string]*ctx.Command{
"paths": &ctx.Command{
Name: "paths [add path]|[del index]|[set index path]|[index]",
Help: "设置文件搜索路径, add: 添加目录, del: 删除目录, set: 修改目录index: 目录序号, path: 目录名",
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { // {{{
if len(arg) == 0 {
for i, v := range nfs.paths {
m.Echo("%d: %s\n", i, v)
}
return
}
switch arg[0] {
case "add":
nfs.paths = append(nfs.paths, arg[1])
case "del":
if i, e := strconv.Atoi(arg[1]); e == nil && i < len(nfs.paths) {
for ; i < len(nfs.paths)-1; i++ {
nfs.paths[i] = nfs.paths[i+1]
}
nfs.paths = nfs.paths[:len(nfs.paths)-1]
}
case "set":
if i, e := strconv.Atoi(arg[1]); e == nil && i < len(nfs.paths) {
nfs.paths[i] = arg[2]
m.Echo("%d: %s\n", i, nfs.paths[i])
}
default:
if i, e := strconv.Atoi(arg[0]); e == nil && i < len(nfs.paths) {
m.Echo("%d: %s\n", i, nfs.paths[i])
}
}
} // }}}
}},
"scan": &ctx.Command{
Name: "scan filename [nfs_name [nfs_help]]",
Help: "扫描文件, filename: 文件名, nfs_name: 模块名, nfs_help: 模块帮助",
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { // {{{
if arg[0] == "stdio" {
m.Optionv("in", os.Stdin)
m.Optionv("out", os.Stdout)
} else {
if f, e := nfs.open(arg[0]); m.Assert(e) {
m.Optionv("in", f)
}
}
m.Start(m.Confx("nfs_name", arg, 1), m.Confx("nfs_help", arg, 2), key, arg[0])
} // }}}
}},
"history": &ctx.Command{
Name: "history [save|load filename [lines [pos]]] [find|search key]",
Help: "扫描记录, save: 保存记录, load: 加载记录, filename: 文件名, lines: 加载或保存记录数量, pos: 加载或保存的起始位置, find: 查找记录, search: 搜索记录, key: 查找或搜索的参数",
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { // {{{
if len(arg) == 0 {
for i, v := range nfs.history {
m.Echo("%d: %s\n", i, v)
}
return
}
switch arg[0] {
case "load":
f, e := nfs.open(arg[1])
m.Assert(e)
defer f.Close()
pos, lines := 0, -1
if len(arg) > 3 {
i, e := strconv.Atoi(arg[3])
m.Assert(e)
pos = i
}
if len(arg) > 2 {
i, e := strconv.Atoi(arg[2])
m.Assert(e)
lines = i
}
bio := bufio.NewScanner(f)
for i := 0; bio.Scan(); i++ {
if i < pos {
continue
}
if lines != -1 && (i-pos) >= lines {
break
}
line := bio.Text()
for _, v := range nfs.history {
if line == v {
line = ""
}
}
if line != "" {
nfs.history = append(nfs.history, line)
}
}
m.Capi("nline", 0, len(nfs.history))
case "save":
f, e := os.Create(arg[1])
// f, e := os.OpenFile(arg[1], os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
m.Assert(e)
defer f.Close()
pos, lines := 0, -1
if len(arg) > 3 {
i, e := strconv.Atoi(arg[3])
m.Assert(e)
pos = i
}
if len(arg) > 2 {
i, e := strconv.Atoi(arg[2])
m.Assert(e)
lines = i
}
for i, v := range nfs.history {
if i < pos {
continue
}
if lines != -1 && (i-pos) >= lines {
break
}
fmt.Fprintln(f, v)
}
case "find":
for i, v := range nfs.history {
if strings.HasPrefix(v, arg[1]) {
m.Echo("%d: %s\n", i, v)
}
}
case "search":
default:
if i, e := strconv.Atoi(arg[0]); e == nil && i < len(nfs.history) {
m.Echo(nfs.history[i])
}
}
} // }}}
}},
"open": &ctx.Command{
Name: "open filename [nfs_name [nfs_help]]",
Help: "打开文件, filename: 文件名, nfs_name: 模块名, nfs_help: 模块帮助",
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if m.Has("io") { // {{{
} else if f, e := os.OpenFile(arg[0], os.O_RDWR|os.O_CREATE, os.ModePerm); m.Assert(e) {
m.Put("option", "in", f).Put("option", "out", f)
}
m.Start(m.Confx("nfs_name", arg, 1), m.Confx("nfs_help", arg, 2), "open", arg[0])
m.Echo(m.Target().Name)
// }}}
}},
"append": &ctx.Command{
Name: "append filename [nfs_name [nfs_help]]",
Help: "打开文件, filename: 文件名, nfs_name: 模块名, nfs_help: 模块帮助",
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if m.Has("io") { // {{{
} else if f, e := os.OpenFile(arg[0], os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm); m.Assert(e) {
m.Put("option", "in", f).Put("option", "out", f)
}
m.Start(m.Confx("nfs_name", arg, 1), m.Confx("nfs_help", arg, 2), "append", arg[0])
m.Echo(m.Target().Name)
// }}}
}},
"read": &ctx.Command{Name: "read [buf_size [pos]]", Help: "读取文件, buf_size: 读取大小, pos: 读取位置", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) && nfs.in != nil { // {{{
n, e := strconv.Atoi(m.Confx("buf_size", arg, 0))
m.Assert(e)
if len(arg) > 1 {
m.Cap("pos", arg[1])
}
buf := make([]byte, n)
if n, e = nfs.in.ReadAt(buf, int64(m.Capi("pos"))); e != io.EOF {
m.Assert(e)
}
m.Capi("nread", n)
m.Echo(string(buf))
if m.Capi("pos", n); n == 0 {
m.Cap("pos", "0")
}
} // }}}
}},
"write": &ctx.Command{Name: "write string [pos]", Help: "写入文件, string: 写入内容, pos: 写入位置", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) && nfs.out != nil { // {{{
if len(arg) > 1 {
m.Cap("pos", arg[1])
}
if len(arg[0]) == 0 {
m.Assert(nfs.out.Truncate(int64(m.Capi("pos"))))
m.Cap("size", m.Cap("pos"))
m.Cap("pos", "0")
} else {
n, e := nfs.out.WriteAt([]byte(arg[0]), int64(m.Capi("pos")))
if m.Capi("nwrite", n); m.Assert(e) && m.Capi("pos", n) > m.Capi("size") {
m.Cap("size", m.Cap("pos"))
}
nfs.out.Sync()
}
m.Echo(m.Cap("pos"))
} // }}}
}},
"load": &ctx.Command{Name: "load file [buf_size [pos]]", Help: "加载文件, buf_size: 加载大小, pos: 加载位置", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if f, e := os.Open(arg[0]); m.Assert(e) { // {{{
defer f.Close()
pos := 0
if len(arg) > 2 {
i, e := strconv.Atoi(arg[2])
m.Assert(e)
pos = i
}
s, e := strconv.Atoi(m.Confx("buf_size", arg, 1))
m.Assert(e)
buf := make([]byte, s)
if l, e := f.ReadAt(buf, int64(pos)); e == io.EOF || m.Assert(e) {
m.Log("info", "read %d", l)
m.Echo(string(buf[:l]))
}
} // }}}
}},
"save": &ctx.Command{Name: "save file string...", Help: "保存文件, file: 保存的文件, string: 保存的内容", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if f, e := os.Create(arg[0]); m.Assert(e) { // {{{
defer f.Close()
for _, v := range arg[1:] {
fmt.Fprint(f, v)
}
} // }}}
}},
"print": &ctx.Command{Name: "print file string...", Help: "输出文件, file: 输出的文件, string: 输出的内容", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if f, e := os.OpenFile(arg[0], os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666); m.Assert(e) { // {{{
defer f.Close()
for _, v := range arg[1:] {
fmt.Fprint(f, v)
}
fmt.Fprint(f, "\n")
} // }}}
}},
"genqr": &ctx.Command{Name: "genqr [qr_size size] filename string...", Help: "生成二维码图片, qr_size: 图片大小, filename: 文件名, string: 输出内容", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if size, e := strconv.Atoi(m.Confx("qr_size")); m.Assert(e) { // {{{
qrcode.WriteFile(strings.Join(arg[1:], ""), qrcode.Medium, size, arg[0])
} // }}}
}},
"json": &ctx.Command{Name: "json [key value]...", Help: "生成格式化内容, key: 参数名, value: 参数值", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if len(arg) == 1 { // {{{
var data interface{}
e := json.Unmarshal([]byte(arg[0]), &data)
m.Assert(e)
buf, e := json.MarshalIndent(data, "", " ")
m.Assert(e)
m.Echo("'")
m.Echo(string(buf))
m.Echo("'")
return
}
if len(arg) > 1 && arg[0] == "file" {
var data interface{}
f, e := os.Open(arg[1])
m.Assert(e)
d := json.NewDecoder(f)
d.Decode(&data)
buf, e := json.MarshalIndent(data, "", " ")
m.Assert(e)
m.Echo(string(buf))
return
}
data := map[string]interface{}{}
for _, k := range m.Meta["option"] {
if v, ok := m.Data[k]; ok {
data[k] = v
continue
}
data[k] = m.Meta[k]
}
for i := 1; i < len(arg)-1; i += 2 {
data[arg[i]] = arg[i+1]
}
buf, e := json.Marshal(data)
m.Assert(e)
m.Echo(string(buf))
// }}}
}},
"pwd": &ctx.Command{Name: "pwd", Help: "查看当前路径", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if m.Options("dir") { // {{{
m.Echo(m.Option("dir"))
return
}
if len(arg) > 0 {
os.Chdir(arg[0])
}
wd, e := os.Getwd()
m.Assert(e)
m.Echo(wd) // }}}
}},
"dir": &ctx.Command{
Name: "dir dir [dir_deep yes|no] [dir_info info] [dir_name name|tree|path|full] [dir_type file|dir] [sort_field name] [sort_order type]",
Help: "查看目录, dir: 目录名, dir_info: 显示统计信息, dir_name: 文件名类型, dir_type: 文件类型, sort_field: 排序字段, sort_order: 排序类型",
Form: map[string]int{"dir_field": 1, "dir_deep": 1, "dir_info": 1, "dir_name": 1, "dir_type": 1, "sort_field": 1, "sort_order": 1},
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
d := "./" + m.Option("dir") // {{{
if len(arg) > 0 {
d = arg[0]
}
if s, e := os.Stat(d); m.Assert(e) && !s.IsDir() {
d = path.Dir(d)
}
fields := strings.Split(m.Confx("dir_field"), " ")
trip := 0
if m.Confx("dir_name") == "path" {
wd, e := os.Getwd()
m.Assert(e)
trip = len(wd) + 1
}
info := strings.Split(m.Confx("dir_info"), " ")
for _, v := range info {
m.Option(v, 0)
}
m.Option("time_layout", "2006-01-02 15:04:05")
dir(m, d, 0, ctx.Right(m.Confx("dir_deep")), fields)
m.Sort(m.Confx("sort_field"), m.Confx("sort_order"))
m.Table(func(maps map[string]string, list []string, line int) bool {
for i, v := range list {
key := m.Meta["append"][i]
switch key {
case "filename":
if trip > 0 {
v = v[trip:]
}
case "dir":
continue
}
m.Echo("%s\t", v)
}
m.Echo("\n")
return true
})
for _, v := range info {
m.Echo("%s: %s\n", v, m.Option(v))
}
// }}}
}},
"git": &ctx.Command{
Name: "git branch|status|diff|log|info arg... [dir path]...",
Help: "版本控制, branch: 分支管理, status: 查看状态, info: 查看分支与状态, dir: 指定路径",
Form: map[string]int{"dir": 1, "git_info": 1, "git_log": 1, "git_log_form": 1},
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if len(arg) == 0 { // {{{
arg = []string{"info"}
}
cmds := []string{arg[0]}
switch arg[0] {
case "s":
arg[0] = "status"
case "b":
arg[0] = "branch"
case "d":
arg[0] = "diff"
}
if arg[0] == "info" {
cmds = strings.Split(m.Confx("git_info"), " ")
}
wd, e := os.Getwd()
m.Assert(e)
if !m.Has("dir") {
m.Option("dir", m.Confx("dir"))
}
for _, p := range m.Meta["dir"] {
if !path.IsAbs(p) {
p = path.Join(wd, p)
}
m.Echo("path: %s\n", p)
for _, c := range cmds {
args := []string{}
switch c {
case "branch", "status", "diff":
if c != "status" {
args = append(args, "--color")
}
args = append(args, strings.Split(m.Confx("git_"+c, arg, 1), " ")...)
if len(arg) > 2 {
args = append(args, arg[2:]...)
}
case "difftool":
cmd := exec.Command("git", "difftool", "-y")
m.Log("info", "cmd: %s %v", "git", "difftool", "-y")
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
if e := cmd.Start(); e != nil {
m.Echo("error: ")
m.Echo("%s\n", e)
} else if e := cmd.Wait(); e != nil {
m.Echo("error: ")
m.Echo("%s\n", e)
}
continue
case "csv":
cmd := exec.Command("git", "log", "--shortstat", "--pretty=commit: %ad", "--date=format:%Y-%m-%d")
m.Log("fuck", "wh%v", cmd)
if out, e := cmd.CombinedOutput(); e != nil {
m.Echo("error: ")
m.Echo("%s\n", e)
} else {
f, e := os.Create(arg[1])
m.Assert(e)
defer f.Close()
type stat struct {
date string
adds int
dels int
}
stats := []*stat{}
list := strings.Split(string(out), "commit: ")
for _, v := range list {
l := strings.Split(v, "\n")
if len(l) > 2 {
fs := strings.Split(strings.Trim(l[2], " "), ", ")
stat := &stat{date: l[0]}
if len(fs) > 2 {
adds := strings.Split(fs[1], " ")
dels := strings.Split(fs[2], " ")
a, e := strconv.Atoi(adds[0])
m.Assert(e)
stat.adds = a
d, e := strconv.Atoi(dels[0])
m.Assert(e)
stat.dels = d
} else {
adds := strings.Split(fs[1], " ")
a, e := strconv.Atoi(adds[0])
m.Assert(e)
if adds[1] == "insertions(+)" {
stat.adds = a
} else {
stat.dels = a
}
}
stats = append(stats, stat)
}
}
fmt.Fprintf(f, "order,date,adds,dels,sum,top,bottom,last\n")
l := len(stats)
for i := 0; i < l/2; i++ {
stats[i], stats[l-i-1] = stats[l-i-1], stats[i]
}
sum := 0
for i, v := range stats {
fmt.Fprintf(f, "%d,%s,%d,%d,%d,%d,%d,%d\n", i, v.date, v.adds, v.dels, sum, sum+v.adds, sum-v.dels, sum+v.adds-v.dels)
sum += v.adds - v.dels
}
}
continue
case "log":
args = append(args, "--color")
args = append(args, strings.Split(m.Confx("git_log"), " ")...)
args = append(args, fmt.Sprintf("--%s", m.Confx("git_log_form")))
args = append(args, m.Confx("git_log_skip", arg, 1, "--skip=%s"))
args = append(args, m.Confx("git_log_line", arg, 2, "-n %s"))
default:
args = append(args, arg[1:]...)
}
m.Log("info", "cmd: %s %v", "git", ctx.Trans("-C", p, c, args))
msg := m.Sess("cli").Cmd("system", "git", "-C", p, c, args)
m.Copy(msg, "result").Copy(msg, "append")
m.Echo("\n")
}
} // }}}
}},
"listen": &ctx.Command{Name: "listen args...", Help: "启动文件服务, args: 参考tcp模块, listen命令的参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if _, ok := m.Target().Server.(*NFS); m.Assert(ok) { //{{{
m.Sess("tcp").Call(func(sub *ctx.Message) *ctx.Message {
sub.Start(fmt.Sprintf("file%d", m.Capi("nfile", 1)), "远程文件")
return sub.Sess("target", m.Source()).Call(func(sub1 *ctx.Message) *ctx.Message {
nfs, _ := sub.Target().Server.(*NFS)
sub1.Remote = make(chan bool, 1)
nfs.send <- sub1
<-sub1.Remote
return nil
})
}, m.Meta["detail"])
}
// }}}
}},
"dial": &ctx.Command{Name: "dial args...", Help: "连接文件服务, args: 参考tcp模块, dial命令的参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if _, ok := m.Target().Server.(*NFS); m.Assert(ok) { //{{{
m.Sess("tcp").Call(func(sub *ctx.Message) *ctx.Message {
sub.Start(fmt.Sprintf("file%d", m.Capi("nfile", 1)), "远程文件")
return sub.Sess("target", m.Source()).Call(func(sub1 *ctx.Message) *ctx.Message {
nfs, _ := sub.Target().Server.(*NFS)
sub1.Remote = make(chan bool, 1)
nfs.send <- sub1
<-sub1.Remote
return nil
})
}, m.Meta["detail"])
}
// }}}
}},
"send": &ctx.Command{Name: "send [file] args...", Help: "连接文件服务, args: 参考tcp模块, dial命令的参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) && nfs.io != nil { // {{{
m.Remote = make(chan bool, 1)
nfs.send <- m
<-m.Remote
} // }}}
}},
},
}
func init() {
nfs := &NFS{}
nfs.Context = Index
ctx.Index.Register(Index, nfs)
}