diff --git a/README.cn.md b/README.cn.md index 3507f1d..64081d5 100644 --- a/README.cn.md +++ b/README.cn.md @@ -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`。 @@ -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`。 diff --git a/README.md b/README.md index e92b208..8309616 100644 --- a/README.md +++ b/README.md @@ -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`. @@ -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`. diff --git a/go.mod b/go.mod index 6611df7..f56e1d2 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 01809a8..0c7ba3b 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/trzsz/filter.go b/trzsz/filter.go index c42fb9c..a6694ff 100644 --- a/trzsz/filter.go +++ b/trzsz/filter.go @@ -42,6 +42,7 @@ import ( "time" "github.com/atotto/clipboard" + "github.com/google/shlex" "github.com/ncruces/zenity" "github.com/trzsz/promptui" ) @@ -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 ). @@ -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. @@ -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") @@ -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 @@ -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)) } @@ -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() } @@ -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 } diff --git a/trzsz/trzsz.go b/trzsz/trzsz.go index 38ac824..380a5fc 100644 --- a/trzsz/trzsz.go +++ b/trzsz/trzsz.go @@ -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)