Skip to content

Commit e554505

Browse files
committed
支持编辑多个文件
1 parent 96d0e7c commit e554505

6 files changed

Lines changed: 144 additions & 90 deletions

File tree

bash/bash.go

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ package bash
33
import (
44
"bufio"
55
"bytes"
6-
"errors"
76
"fmt"
87
"io"
98
"net"
109
"strings"
1110
"sync"
1211

1312
"github.com/SierraSoftworks/multicast/v2"
14-
"github.com/alessio/shellescape"
1513
"github.com/google/uuid"
1614
"github.com/jellydator/ttlcache/v3"
1715
"github.com/lainio/err2"
@@ -32,14 +30,6 @@ type Bash struct {
3230
ID string
3331
}
3432

35-
type ID interface {
36-
No() string
37-
NameSapce() string
38-
String() string
39-
Close() error
40-
}
41-
type IDGenerator func(id string, pwd string) (ID, error)
42-
4333
func NewBash() *Bash {
4434
return &Bash{
4535
clients: ttlcache.New[string, *webdav.Client](),
@@ -107,59 +97,6 @@ func (sh *Bash) serve(conn net.Conn) (err error) {
10797
return
10898
}
10999

110-
func (sh *Bash) Connect(r *bufio.Reader, conn net.Conn) (err error) {
111-
defer conn.Close()
112-
defer err2.Handle(&err, func() {
113-
if errors.Is(err, io.EOF) {
114-
return
115-
}
116-
if errors.Is(err, webdav.ErrNeedPrint) {
117-
cmd := fmt.Sprintf(">&2 echo lo: %s\n", shellescape.Quote(err.Error()))
118-
io.WriteString(conn, cmd)
119-
return
120-
}
121-
if errors.Is(err, webdav.ErrPrintHelp) {
122-
return
123-
}
124-
fmt.Println("client connect", err)
125-
})
126-
127-
io.WriteString(conn, "export PS1=''\n")
128-
129-
c := webdav.NewClient(conn)
130-
131-
To(c.Open(r, sh.VERSION, sh.ID))
132-
defer c.Close()
133-
idRaw := strings.ToLower(c.ID)
134-
135-
tmpID := uuid.NewString()
136-
c.ID = tmpID
137-
sh.clients.Set(tmpID, c, ttlcache.NoTTL) // we can exec cmd after set client
138-
defer sh.clients.Delete(tmpID)
139-
140-
id := To1(sh.IDGenerator(idRaw, c.PWD))
141-
defer id.Close()
142-
if !strings.HasPrefix(idRaw, id.No()+"-") && sh.VERSION != "dev" { // no 不一致时覆盖已有的 no
143-
// 开发环境不更新原有的 no
144-
To(c.StoreID(id.No()))
145-
}
146-
sh.clients.Delete(tmpID)
147-
c.ID = id.String()
148-
sh.clients.Set(c.ID, c, ttlcache.NoTTL)
149-
defer sh.clients.Delete(c.ID)
150-
151-
sh.connected.C <- c
152-
153-
for {
154-
_, _, err = r.ReadLine()
155-
if err != nil {
156-
break
157-
}
158-
// fmt.Println("line:", string(line))
159-
}
160-
return
161-
}
162-
163100
func (sh *Bash) BindStderr(v string, r *bufio.Reader, conn net.Conn) (err error) {
164101
defer err2.Handle(&err)
165102
defer conn.Close()

bash/connect.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package bash
2+
3+
import (
4+
"bufio"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"net"
9+
"strings"
10+
11+
"github.com/alessio/shellescape"
12+
"github.com/google/uuid"
13+
"github.com/jellydator/ttlcache/v3"
14+
"github.com/lainio/err2"
15+
. "github.com/lainio/err2/try"
16+
"github.com/vscode-lcode/lcode/v2/bash/webdav"
17+
)
18+
19+
type ID interface {
20+
No() string
21+
NameSapce() string
22+
String() string
23+
Close() error
24+
}
25+
type LcodeClient interface {
26+
RawID() string
27+
PWD() string
28+
Targets() []string
29+
}
30+
type IDGenerator func(client LcodeClient) (ID, error)
31+
32+
func (sh *Bash) Connect(r *bufio.Reader, conn net.Conn) (err error) {
33+
defer conn.Close()
34+
defer err2.Handle(&err, func() {
35+
if errors.Is(err, io.EOF) {
36+
return
37+
}
38+
if errors.Is(err, webdav.ErrNeedPrint) {
39+
cmd := fmt.Sprintf(">&2 echo lo: %s\n", shellescape.Quote(err.Error()))
40+
io.WriteString(conn, cmd)
41+
return
42+
}
43+
if errors.Is(err, webdav.ErrPrintHelp) {
44+
return
45+
}
46+
fmt.Println("client connect", err)
47+
})
48+
49+
io.WriteString(conn, "export PS1=''\n")
50+
51+
c := webdav.NewClient(conn)
52+
53+
To(c.Open(r, sh.VERSION, sh.ID))
54+
defer c.Close()
55+
lc := LcodeClientWrapper{c, c.ID}
56+
57+
tmpID := uuid.NewString()
58+
c.ID = tmpID
59+
sh.clients.Set(tmpID, c, ttlcache.NoTTL) // we can exec cmd after set client
60+
defer sh.clients.Delete(tmpID)
61+
62+
id := To1(sh.IDGenerator(lc))
63+
defer id.Close()
64+
if !strings.HasPrefix(lc.RawID(), id.No()+"-") && sh.VERSION != "dev" { // no 不一致时覆盖已有的 no
65+
// 开发环境不更新原有的 no
66+
To(c.StoreID(id.No()))
67+
}
68+
sh.clients.Delete(tmpID)
69+
c.ID = id.String()
70+
sh.clients.Set(c.ID, c, ttlcache.NoTTL)
71+
defer sh.clients.Delete(c.ID)
72+
73+
sh.connected.C <- c
74+
75+
for {
76+
_, _, err = r.ReadLine()
77+
if err != nil {
78+
break
79+
}
80+
// fmt.Println("line:", string(line))
81+
}
82+
return
83+
}
84+
85+
type LcodeClientWrapper struct {
86+
*webdav.Client
87+
rawID string
88+
}
89+
90+
var _ LcodeClient = (*LcodeClientWrapper)(nil)
91+
92+
func (lc LcodeClientWrapper) RawID() string { return lc.rawID }
93+
func (lc LcodeClientWrapper) PWD() string { return lc.Client.PWD }

bash/webdav/client.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"fmt"
1010
"io"
1111
"net"
12+
"os"
1213
"path/filepath"
1314
"strings"
15+
"sync"
1416
"time"
1517

1618
"github.com/SierraSoftworks/multicast/v2"
@@ -26,7 +28,10 @@ type Client struct {
2628
ID string
2729
tasks *ttlcache.Cache[string, chan net.Conn]
2830

29-
PWD string // 也有可能是文件, 用途是用于限制文件访问
31+
PWD string
32+
targets []string
33+
targetsInit *sync.Once
34+
3035
Logger func(file *File, err error)
3136
closed *multicast.Channel[any]
3237

@@ -78,7 +83,6 @@ func (c *Client) Open(r *bufio.Reader, version string, id string) (err error) {
7883

7984
func (c *Client) intFlag(version string) *flag.FlagSet {
8085
f := flag.NewFlagSet("lcode@"+version, flag.ContinueOnError)
81-
f.StringVar(&c.PWD, "pwd", ".", "工作目录")
8286
f.StringVar(&c.ServerAddr, "server", c.conn.LocalAddr().String(), "server addr")
8387
f.Bool("x", true, "仅用以分割bash参数, 不作其他用途")
8488
return f
@@ -102,9 +106,7 @@ func (c *Client) parseArgs(r *bufio.Reader, version string) (err error) {
102106
return
103107
}
104108

105-
if f.Arg(0) != "" {
106-
f.Set("pwd", f.Arg(0))
107-
}
109+
c.targets = f.Args()
108110
return
109111
}
110112

@@ -153,24 +155,42 @@ func (c *Client) initPWD(r *bufio.Reader) (err error) {
153155
defer err2.Handle(&err, func() {
154156
c.log(fmt.Errorf("init pwd failed: %w", err))
155157
})
156-
cmd := fmt.Sprintf("TZ=UTC0 ls -Ald --full-time %s || echo /dev/null | head -n 1\n", shellescape.Quote(c.PWD))
157-
To1(io.WriteString(c.conn, cmd))
158-
line, _ := To2(r.ReadLine())
159-
if string(line) == "/dev/null" {
160-
err = ErrEditTargetNotExists
161-
return
162-
}
163-
f := parseLsLine(line)
164-
if strings.HasPrefix(f.sys[7], "/") {
165-
c.PWD = f.sys[7]
166-
return
167-
}
168158
To1(io.WriteString(c.conn, "pwd\n"))
169159
pwd, _ := To2(r.ReadLine())
170-
c.PWD = filepath.Join(string(pwd), f.name)
160+
c.PWD = string(pwd)
161+
c.targetsInit = &sync.Once{}
171162
return
172163
}
173164

165+
// 要编辑的目标文件, 用途是用于限制文件访问
166+
func (c *Client) Targets() []string {
167+
c.targetsInit.Do(func() {
168+
if len(c.targets) == 0 {
169+
c.targets = []string{c.PWD}
170+
return
171+
}
172+
var targets = make([]string, 0)
173+
defer func() { c.targets = targets }()
174+
for _, t := range c.targets {
175+
f := OpenFile(c, t)
176+
stat, err := f.Stat()
177+
switch {
178+
case errors.Is(err, os.ErrNotExist):
179+
targets = append(targets, "/dev/null"+t)
180+
case err != nil:
181+
targets = append(targets, "/dev/err"+t)
182+
default:
183+
fullpath := stat.Sys().([]string)[7]
184+
if !strings.HasPrefix(fullpath, "/") {
185+
fullpath = filepath.Join(c.PWD, fullpath)
186+
}
187+
targets = append(targets, fullpath)
188+
}
189+
}
190+
})
191+
return c.targets
192+
}
193+
174194
func (c *Client) Close() {
175195
c.tasks.DeleteAll()
176196
c.tasks.Stop()

hub/access.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ func (hub *Hub) AllowDir(host Host, filepath string) (client *Client, err error)
1616
}
1717
dirs := item.Value()
1818
for _, item := range dirs {
19-
if allow := strings.HasPrefix(filepath, item.Workdir); allow {
20-
client = &item
21-
return
19+
for _, target := range item.Targets {
20+
if allow := strings.HasPrefix(filepath, target); allow {
21+
client = &item
22+
return
23+
}
2224
}
2325
}
2426
return

hub/id.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ import (
1515

1616
var _ bash.IDGenerator = (*Hub)(nil).IDGenerator
1717

18-
func (hub *Hub) IDGenerator(idRaw string, pwd string) (id bash.ID, err error) {
18+
func (hub *Hub) IDGenerator(c bash.LcodeClient) (id bash.ID, err error) {
1919
defer err2.Handle(&err)
20-
host := parseIDRaw(idRaw)
20+
host := parseIDRaw(c.RawID())
2121
if host.No == 0 {
2222
To(hub.addHost(&host))
2323
}
2424
client := &Client{
2525
Namespace: host.Namespace,
2626
No2: host.No,
27-
Workdir: pwd,
27+
Workdir: c.PWD(),
28+
Targets: c.Targets(),
2829
}
2930
To(hub.addClient(client))
3031
hub.setHostLocker(client.ToHost())

hub/model.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ type Client struct {
1818
hub *Hub
1919

2020
Id int64
21-
Namespace string `xorm:"notnull"`
22-
No2 uint32 `xorm:"notnull 'no'"`
23-
Workdir string `xorm:"notnull"`
21+
Namespace string `xorm:"notnull"`
22+
No2 uint32 `xorm:"notnull 'no'"`
23+
Workdir string `xorm:"notnull"`
24+
Targets []string `xorm:"notnull"`
2425
}
2526

2627
func Sync(eg *xorm.Engine) (err error) {

0 commit comments

Comments
 (0)