Skip to content

Commit

Permalink
support custom drag file upload command
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Jul 13, 2024
1 parent a7ad81a commit bb31ecf
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 100 deletions.
29 changes: 16 additions & 13 deletions README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,6 @@ _有关 `trzsz ( trz / tsz )` 更详细的文档,请查看 [https://trzsz.gith

- 如果在本地电脑使用 `tmux`,先不带 `trzsz` 运行 `tmux`,然后再使用 `trzsz ssh` 登录远程服务器。

## 可配置项

`trzsz` 使用的配置文件是 `~/.trzsz.conf`( Windows 是 `C:\Users\your_name\.trzsz.conf` )。注意路径必须包含 `/` 结尾,如:

```
DefaultUploadPath =
DefaultDownloadPath = /Users/username/Downloads/
```

- 如果 `DefaultUploadPath` 不为空,上传选择文件时会默认打开此目录。

- 如果 `DefaultDownloadPath` 不为空,下载文件时会自动下载到此目录( 不需要再弹窗选择路径 )。

## 支持 Zmodem

- 使用 `-z``--zmodem` 启用 `rz / sz` 功能,例如 `trzsz -z ssh remote_server`
Expand All @@ -210,6 +197,22 @@ DefaultDownloadPath = /Users/username/Downloads/

- 在 Linux 系统,剪贴板集成功能需要安装 `xclip``xsel` 命令。

## 可配置项

`trzsz` 使用的配置文件是 `~/.trzsz.conf`( Windows 是 `C:\Users\your_name\.trzsz.conf` ),如:

```
DefaultUploadPath =
DefaultDownloadPath = /Users/username/Downloads/
DragFileUploadCommand = trz -y
```

- 如果 `DefaultUploadPath` 不为空,上传选择文件时会默认打开此目录。

- 如果 `DefaultDownloadPath` 不为空,下载文件时会自动下载到此目录( 不需要再弹窗选择路径 )。

- `DragFileUploadCommand` 的默认值是 `trz`,如果想上传时覆盖现有文件可配置成 `trz -y`,如果想使用 `rz` 上传可配置成 `rz`

## 常见问题

- 如果 [MSYS2](https://www.msys2.org/)[Git Bash](https://www.atlassian.com/git/tutorials/git-bash) 遇到错误 `The handle is invalid`
Expand Down
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,6 @@ _Please check [https://trzsz.github.io](https://trzsz.github.io) for more inform
- If using `tmux` on the local computer, run `tmux` ( without `trzsz` ) first, then `trzsz ssh` to login.
## Configuration
`trzsz` looks for configuration at `~/.trzsz.conf` ( `C:\Users\your_name\.trzsz.conf` on Windows ). The path have to end with `/`, e.g.:
```
DefaultUploadPath =
DefaultDownloadPath = /Users/username/Downloads/
```
- If the `DefaultUploadPath` is not empty, the path will be opened by default while choosing upload files.
- If the `DefaultDownloadPath` is not empty, downloading files will be saved to the path automatically instead of asking each time.
## Zmodem support
- Use `-z` or `--zmodem` to enable the `rz / sz` feature. e.g., `trzsz -z ssh remote_server`.
Expand All @@ -210,6 +197,22 @@ DefaultDownloadPath = /Users/username/Downloads/
- On Linux, clipboard integration requires `xclip` or `xsel` command to be installed.
## Configuration
`trzsz` looks for configuration at `~/.trzsz.conf` ( `C:\Users\your_name\.trzsz.conf` on Windows ), e.g.:
```
DefaultUploadPath =
DefaultDownloadPath = /Users/username/Downloads/
DragFileUploadCommand = trz -y
```
- If the `DefaultUploadPath` is not empty, the path will be opened by default while choosing upload files.
- If the `DefaultDownloadPath` is not empty, downloading files will be saved to the path automatically instead of asking each time.
- The default value of `DragFileUploadCommand` is `trz`, configure it to `trz -y` if you want to overwrite the existing files, configure it to `rz` if you want to use `rz` to upload.
## Trouble shooting
- If using [MSYS2](https://www.msys2.org/) or [Git Bash](https://www.atlassian.com/git/tutorials/git-bash) on windows, and getting an error `The handle is invalid`.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/UserExistsError/conpty v0.1.4
github.com/atotto/clipboard v0.1.4
github.com/creack/pty v1.1.21
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/klauspost/compress v1.17.9
github.com/mattn/go-runewidth v0.0.15
github.com/ncruces/zenity v0.10.13
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f h1:OGqDDftRTwrvUoL6pOG7rYTmWsTCvyEWFsMjg+HcOaA=
github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f/go.mod h1:Dv9D0NUlAsaQcGQZa5kc5mqR9ua72SmA8VXi4cd+cBw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8=
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
Expand Down
174 changes: 100 additions & 74 deletions trzsz/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"time"

"github.com/atotto/clipboard"
"github.com/google/shlex"
"github.com/ncruces/zenity"
"github.com/trzsz/promptui"
)
Expand All @@ -64,27 +65,30 @@ type TrzszOptions struct {

// TrzszFilter is a filter that supports trzsz ( trz / tsz ).
type TrzszFilter struct {
clientIn io.Reader
clientOut io.WriteCloser
serverIn io.WriteCloser
serverOut io.Reader
options TrzszOptions
transfer atomic.Pointer[trzszTransfer]
zmodem atomic.Pointer[zmodemTransfer]
progress atomic.Pointer[textProgressBar]
promptPipe atomic.Pointer[io.PipeWriter]
trigger *trzszTrigger
dragging atomic.Bool
dragHasDir atomic.Bool
dragMutex sync.Mutex
dragFiles []string
interrupting atomic.Bool
skipTrzCommand atomic.Bool
logger *traceLogger
defaultUploadPath atomic.Pointer[string]
defaultDownloadPath atomic.Pointer[string]
tunnelConnector atomic.Pointer[func(int) net.Conn]
osc52Sequence *bytes.Buffer
clientIn io.Reader
clientOut io.WriteCloser
serverIn io.WriteCloser
serverOut io.Reader
options TrzszOptions
transfer atomic.Pointer[trzszTransfer]
zmodem atomic.Pointer[zmodemTransfer]
progress atomic.Pointer[textProgressBar]
promptPipe atomic.Pointer[io.PipeWriter]
trigger *trzszTrigger
dragging atomic.Bool
dragHasDir atomic.Bool
dragMutex sync.Mutex
dragFiles []string
interrupting atomic.Bool
skipUploadCommand atomic.Bool
uploadCommandIsNotTrz atomic.Bool
logger *traceLogger
defaultUploadPath atomic.Pointer[string]
defaultDownloadPath atomic.Pointer[string]
dragFileUploadCommand atomic.Pointer[string]
currentUploadCommand atomic.Pointer[string]
tunnelConnector atomic.Pointer[func(int) net.Conn]
osc52Sequence *bytes.Buffer
}

// NewTrzszFilter create a TrzszFilter to support trzsz ( trz / tsz ).
Expand Down Expand Up @@ -162,13 +166,41 @@ func (filter *TrzszFilter) UploadFiles(filePaths []string) error {
}

// SetDefaultUploadPath set the default open path while choosing upload files.
func (filter *TrzszFilter) SetDefaultUploadPath(uploadPath string) {
filter.defaultUploadPath.Store(&uploadPath)
func (filter *TrzszFilter) SetDefaultUploadPath(path string) {
if path == "" {
filter.defaultUploadPath.Store(&path)
return
}
path = resolveHomeDir(path)
if !strings.HasSuffix(path, string(os.PathSeparator)) {
path += string(os.PathSeparator)
}
filter.defaultUploadPath.Store(&path)
}

// SetDefaultDownloadPath set the path to automatically save while downloading files.
func (filter *TrzszFilter) SetDefaultDownloadPath(downloadPath string) {
filter.defaultDownloadPath.Store(&downloadPath)
func (filter *TrzszFilter) SetDefaultDownloadPath(path string) {
if path == "" {
filter.defaultDownloadPath.Store(&path)
return
}
path = resolveHomeDir(path)
filter.defaultDownloadPath.Store(&path)
}

// SetDragFileUploadCommand set the command to execute while dragging files to upload.
func (filter *TrzszFilter) SetDragFileUploadCommand(command string) {
filter.uploadCommandIsNotTrz.Store(false)
if command != "" {
tokens, err := shlex.Split(command)
if err == nil && len(tokens) > 0 {
name := filepath.Base(tokens[0])
if name != "trz" && name != "trz.exe" {
filter.uploadCommandIsNotTrz.Store(true)
}
}
}
filter.dragFileUploadCommand.Store(&command)
}

// SetTunnelConnector set the connector for tunnel transferring.
Expand All @@ -180,60 +212,41 @@ func (filter *TrzszFilter) SetTunnelConnector(connector func(int) net.Conn) {
filter.tunnelConnector.Store(&connector)
}

func (filter *TrzszFilter) getTrzszConfig(name string) string {
func (filter *TrzszFilter) readTrzszConfig() {
home, err := os.UserHomeDir()
if err != nil {
return ""
return
}
file, err := os.Open(filepath.Join(home, ".trzsz.conf"))
if err != nil {
return ""
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
lowerName := strings.ToLower(name)
for scanner.Scan() {
line := scanner.Text()
idx := strings.Index(line, "=")
idx := strings.Index(line, "#")
if idx >= 0 {
line = line[:idx]
}
idx = strings.Index(line, "=")
if idx < 0 {
continue
}
if strings.ToLower(strings.TrimSpace(line[:idx])) == lowerName {
return strings.TrimSpace(line[idx+1:])
name := strings.ToLower(strings.TrimSpace(line[:idx]))
value := strings.TrimSpace(line[idx+1:])
if name == "" || value == "" {
continue
}
switch {
case name == "defaultuploadpath" && filter.defaultUploadPath.Load() == nil:
filter.SetDefaultUploadPath(value)
case name == "defaultdownloadpath" && filter.defaultDownloadPath.Load() == nil:
filter.SetDefaultDownloadPath(value)
case name == "dragfileuploadcommand" && filter.dragFileUploadCommand.Load() == nil:
filter.SetDragFileUploadCommand(value)
}
}
return ""
}

func (filter *TrzszFilter) getDefaultUploadPath() string {
path := ""
if p := filter.defaultUploadPath.Load(); p != nil {
path = *p
}
if path == "" {
path = filter.getTrzszConfig("DefaultUploadPath")
}
if path == "" {
return ""
}
if !strings.HasSuffix(path, string(os.PathSeparator)) {
path += string(os.PathSeparator)
}
return resolveHomeDir(path)
}

func (filter *TrzszFilter) getDefaultDownloadPath() string {
path := ""
if p := filter.defaultDownloadPath.Load(); p != nil {
path = *p
}
if path == "" {
path = filter.getTrzszConfig("DefaultDownloadPath")
}
if path == "" {
return ""
}
return resolveHomeDir(path)
}

var errUserCanceled = fmt.Errorf("Cancelled")
Expand Down Expand Up @@ -267,7 +280,10 @@ func zenityErrorWithTips(err error) error {
}

func (filter *TrzszFilter) chooseDownloadPath() (string, error) {
savePath := filter.getDefaultDownloadPath()
savePath := ""
if path := filter.defaultDownloadPath.Load(); path != nil {
savePath = *path
}
if savePath != "" {
time.Sleep(50 * time.Millisecond) // wait for all output to show
return savePath, nil
Expand Down Expand Up @@ -299,7 +315,10 @@ func (filter *TrzszFilter) chooseUploadPaths(directory bool) ([]string, error) {
zenity.Title("Choose some files to send"),
zenity.ShowHidden(),
}
defaultPath := filter.getDefaultUploadPath()
defaultPath := ""
if path := filter.defaultUploadPath.Load(); path != nil {
defaultPath = *path
}
if defaultPath != "" {
options = append(options, zenity.Filename(defaultPath))
}
Expand Down Expand Up @@ -484,12 +503,19 @@ func (filter *TrzszFilter) uploadDragFiles() {
_ = writeAll(filter.serverIn, []byte{0x03})
time.Sleep(200 * time.Millisecond)
filter.interrupting.Store(false)
filter.skipTrzCommand.Store(true)
if filter.dragHasDir.Load() {
_ = writeAll(filter.serverIn, []byte("trz -d\r"))
} else {
_ = writeAll(filter.serverIn, []byte("trz\r"))
filter.skipUploadCommand.Store(true)
command := ""
if cmd := filter.dragFileUploadCommand.Load(); cmd != nil {
command = *cmd
}
if command == "" {
command = "trz"
}
if filter.dragHasDir.Load() && !filter.uploadCommandIsNotTrz.Load() {
command += " -d"
}
filter.currentUploadCommand.Store(&command)
_ = writeAll(filter.serverIn, []byte(command+"\r"))
time.Sleep(3 * time.Second)
filter.resetDragFiles()
}
Expand Down Expand Up @@ -716,10 +742,10 @@ func (filter *TrzszFilter) wrapOutput() {
if filter.interrupting.Load() {
continue
}
if filter.skipTrzCommand.Load() {
filter.skipTrzCommand.Store(false)
if filter.skipUploadCommand.Load() {
filter.skipUploadCommand.Store(false)
output := strings.TrimRight(string(trimVT100(buf)), "\r\n")
if output == "trz" || output == "trz -d" {
if command := filter.currentUploadCommand.Load(); command != nil && *command == output {
_ = writeAll(filter.clientOut, []byte("\r\n"))
continue
}
Expand Down
1 change: 1 addition & 0 deletions trzsz/trzsz.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func TrzszMain() int {
EnableZmodem: args.Zmodem,
EnableOSC52: args.OSC52,
})
filter.readTrzszConfig()
pty.OnResize(filter.SetTerminalColumns)
// handle signal
go handleSignal(pty, filter)
Expand Down

0 comments on commit bb31ecf

Please sign in to comment.