Skip to content

Commit

Permalink
drag files to upload
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Jun 11, 2022
1 parent 7dececd commit 38f3cb6
Show file tree
Hide file tree
Showing 9 changed files with 479 additions and 80 deletions.
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,36 @@ sudo make install

### on Local

Add `trzsz` before the shell, e.g.:
Install the `trzsz` binary to one of the `PATH` directory.

Add `trzsz` before the shell to support trzsz ( trz / tsz ), e.g.:

```sh
trzsz tmux
trzsz /bin/bash
trzsz ssh x.x.x.x
trzsz bash
trzsz.exe cmd
trzsz.exe ssh x.x.x.x
trzsz ssh x.x.x.x
```

Add `trzsz --dragfile` before the `ssh` to enable drag files to upload, e.g.:

```sh
trzsz -d ssh x.x.x.x
trzsz --dragfile ssh x.x.x.x
```


### on Server

Install the `trz` and `tsz` binaries to one of the `PATH` directory.

Similar to lrzsz ( rz / sz ), command `trz` to upload files, command `tsz /path/to/file` to download files.

For more information, see the website of trzsz: [https://trzsz.github.io](https://trzsz.github.io/).


## Suggestion

* It is recommended to set alias `alias ssh="trzsz ssh"` for convenience.
* It is recommended to set `alias ssh="trzsz ssh"` for convenience, `alias ssh="trzsz -d ssh"` for dragging files.

* If using `tmux` on the local computer, run `tmux` ( without `trzsz` ) first, then `trzsz ssh` to login.

Expand All @@ -79,9 +88,16 @@ DefaultDownloadPath = /Users/username/Downloads/
* `Git Bash` should have winpty installed, no need to install it manually.
* Add `winpty` before `trzsz`, e.g.: `winpty trzsz ssh x.x.x.x`.

* `/usr/bin/ssh` in [MSYS2](https://www.msys2.org/) and [Cygwin](https://www.cygwin.com/) is not supported yet, use the [OpenSSH](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse) instead.
* in `MSYS2`, e.g.: `winpty trzsz /c/Windows/System32/OpenSSH/ssh.exe x.x.x.x`.
* in `Cygwin`, e.g.: `trzsz "C:\Windows\System32\OpenSSH\ssh.exe" x.x.x.x`.
* The `/usr/bin/ssh` in [MSYS2](https://www.msys2.org/) and [Cygwin](https://www.cygwin.com/) is not supported yet, use the [OpenSSH](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse) instead.
* In `MSYS2`, e.g.: `winpty trzsz /c/Windows/System32/OpenSSH/ssh.exe x.x.x.x`.
* In `Cygwin`, e.g.: `trzsz "C:\Windows\System32\OpenSSH\ssh.exe" x.x.x.x`.

* Dragging files doesn't upload?
* Don't forget the `--dragfile` option. e.g.: `trzsz -d ssh x.x.x.x`.
* Make sure the `trz` in one of the `PATH` directory on the server.
* On Windows, make sure there is no `Administrator` on the title.
* The `cmd` and `PowerShell` only support draging one file into it.
* On the Windows Terminal, drag files to the top left where shows `Paste path to file`.


## Screenshot
Expand All @@ -96,6 +112,11 @@ DefaultDownloadPath = /Users/username/Downloads/
![ubuntu trzsz ssh](https://trzsz.github.io/images/ubuntu_trzsz.gif)


#### Drag files

![drag files ssh](https://trzsz.github.io/images/drag_files.gif)


## Contact

Feel free to email me <lonnywong@qq.com>.
10 changes: 0 additions & 10 deletions trzsz/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,6 @@ func (b *TrzszBuffer) readBinary(size int, timeout <-chan time.Time) ([]byte, er
return b.readBuf.Bytes(), nil
}

func isVT100End(b byte) bool {
if 'a' <= b && b <= 'z' {
return true
}
if 'A' <= b && b <= 'Z' {
return true
}
return false
}

func isTrzszLetter(b byte) bool {
if 'a' <= b && b <= 'z' {
return true
Expand Down
37 changes: 37 additions & 0 deletions trzsz/comm.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,18 @@ import (
"syscall"
)

var isLinux bool = (runtime.GOOS == "linux")
var isMacOS bool = (runtime.GOOS == "darwin")
var isWindows bool = (runtime.GOOS == "windows")

func IsLinux() bool {
return isLinux
}

func IsMacOS() bool {
return isMacOS
}

func IsWindows() bool {
return isWindows
}
Expand Down Expand Up @@ -330,3 +340,30 @@ func handleServerSignal(transfer *TrzszTransfer) {
transfer.stopTransferringFiles()
}()
}

func isVT100End(b byte) bool {
if 'a' <= b && b <= 'z' {
return true
}
if 'A' <= b && b <= 'Z' {
return true
}
return false
}

func trimVT100(buf []byte) []byte {
b := new(bytes.Buffer)
skipVT100 := false
for _, c := range buf {
if skipVT100 {
if isVT100End(c) {
skipVT100 = false
}
} else if c == '\x1b' {
skipVT100 = true
} else {
b.WriteByte(c)
}
}
return b.Bytes()
}
233 changes: 233 additions & 0 deletions trzsz/drag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
MIT License
Copyright (c) 2022 Lonny Wong
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package trzsz

import (
"bytes"
"errors"
"fmt"
"os"
"strings"
)

func detectDragFiles(buf []byte) ([]string, bool) {
if IsLinux() {
return detectDragFilesOnLinux(buf)
} else if IsMacOS() {
return detectDragFilesOnMacOS(buf)
} else if IsWindows() {
return detectDragFilesOnWindows(buf)
}
return nil, false
}

func detectFilePath(path string, dragFiles *[]string, hasDir *bool) bool {
fileInfo, err := os.Stat(path)
if errors.Is(err, os.ErrNotExist) {
return false
}
if fileInfo.IsDir() {
*hasDir = true
*dragFiles = append(*dragFiles, path)
return true
}
if fileInfo.Mode().IsRegular() {
*dragFiles = append(*dragFiles, path)
return true
}
return false
}

func detectDragFilesOnLinux(buf []byte) ([]string, bool) {
length := len(buf)
if length < 5 || buf[0] != '\'' || buf[1] != '/' || buf[length-1] != ' ' || buf[length-2] != '\'' {
return nil, false
}
hasDir := false
var dragFiles []string
paths := strings.Split(string(buf[1:length-2]), "' '")
for _, path := range paths {
if !detectFilePath(path, &dragFiles, &hasDir) {
return nil, false
}
}
return dragFiles, hasDir
}

func detectDragFilesOnMacOS(buf []byte) ([]string, bool) {
length := len(buf)
if length < 3 || buf[0] != '/' || buf[length-1] != ' ' || buf[length-2] == '\\' {
return nil, false
}
hasDir := false
var dragFiles []string
pathBuf := new(bytes.Buffer)
for i := 0; i < length; i++ {
if buf[i] == ' ' {
path := string(pathBuf.Bytes())
if !detectFilePath(path, &dragFiles, &hasDir) {
return nil, false
}
pathBuf.Reset()
} else if buf[i] == '\\' {
i++
if i < length {
pathBuf.WriteByte(buf[i])
}
} else {
pathBuf.WriteByte(buf[i])
}
}
if pathBuf.Len() != 0 {
return nil, false
}
return dragFiles, hasDir
}

func detectDragFilesOnWindows(buf []byte) ([]string, bool) {
length := len(buf)
if length < 4 {
return nil, false
}
hasDir := false
var dragFiles []string
if buf[length-1] == '"' && buf[0] >= 'A' && buf[0] <= 'Z' && buf[1] == ':' && buf[2] == '\\' &&
bytes.IndexByte(buf[:length-1], '"') < 0 {
// Cmd & PowerShell may lost the first `"`, and supports one path only.
if detectFilePath(string(buf[:length-1]), &dragFiles, &hasDir) {
return dragFiles, hasDir
}
}
isWinPath, isMsysPath, isCygPath := false, false, false
if (buf[0] == '"' && buf[1] >= 'A' && buf[1] <= 'Z' && buf[2] == ':' && buf[3] == '\\') ||
(buf[0] >= 'A' && buf[0] <= 'Z' && buf[1] == ':' && buf[2] == '\\') {
isWinPath = true
} else if (buf[0] == '\'' && buf[1] == '/' && buf[2] >= 'a' && buf[2] <= 'z' && buf[3] == '/') ||
(buf[0] == '/' && buf[1] >= 'a' && buf[1] <= 'z' && buf[2] == '/') {
isMsysPath = true
} else if (length > 13 && string(buf[:11]) == "'/cygdrive/" && buf[11] >= 'a' && buf[11] <= 'z' && buf[12] == '/') ||
(length > 12 && string(buf[:10]) == "/cygdrive/" && buf[10] >= 'a' && buf[10] <= 'z' && buf[11] == '/') {
isCygPath = true
} else {
return nil, false
}
var i int
var path string
for idx := 0; idx < length; idx += i {
if isWinPath {
path, i = nextWinPath(buf[idx:])
} else if isMsysPath {
path, i = nextMsysPath(buf[idx:])
} else if isCygPath {
path, i = nextCygPath(buf[idx:])
}
if path == "" {
return nil, false
}
if !detectFilePath(path, &dragFiles, &hasDir) {
return nil, false
}
}
return dragFiles, hasDir
}

func nextWinPath(buf []byte) (string, int) {
length := len(buf)
if length < 4 {
return "", 0
}
if buf[0] == '"' && buf[1] >= 'A' && buf[1] <= 'Z' && buf[2] == ':' && buf[3] == '\\' {
idx := bytes.IndexByte(buf[1:], '"')
if idx < 0 {
return "", 0
}
idx++
if idx+1 < length && buf[idx+1] != ' ' {
return "", 0
}
return string(buf[1:idx]), idx + 2
} else if buf[0] >= 'A' && buf[0] <= 'Z' && buf[1] == ':' && buf[2] == '\\' {
idx := bytes.IndexByte(buf, ' ')
if idx < 0 {
return string(buf), length
}
return string(buf[:idx]), idx + 1
}
return "", 0
}

func nextMsysPath(buf []byte) (string, int) {
length := len(buf)
if length < 4 {
return "", 0
}
if buf[0] == '\'' && buf[1] == '/' && buf[2] >= 'a' && buf[2] <= 'z' && buf[3] == '/' {
idx := bytes.IndexByte(buf[1:], '\'')
if idx < 0 {
return "", 0
}
idx++
if idx+1 < length && buf[idx+1] != ' ' {
return "", 0
}
return unixPathToWinPath(buf[1:idx]), idx + 2
} else if buf[0] == '/' && buf[1] >= 'a' && buf[1] <= 'z' && buf[2] == '/' {
idx := bytes.IndexByte(buf, ' ')
if idx < 0 {
return unixPathToWinPath(buf), length
}
return unixPathToWinPath(buf[:idx]), idx + 1
}
return "", 0
}

func nextCygPath(buf []byte) (string, int) {
length := len(buf)
if length < 13 {
return "", 0
}
if string(buf[:11]) == "'/cygdrive/" && buf[11] >= 'a' && buf[11] <= 'z' && buf[12] == '/' {
idx := bytes.IndexByte(buf[1:], '\'')
if idx < 0 {
return "", 0
}
idx++
if idx+1 < length && buf[idx+1] != ' ' {
return "", 0
}
return unixPathToWinPath(buf[10:idx]), idx + 2
} else if string(buf[:10]) == "/cygdrive/" && buf[10] >= 'a' && buf[10] <= 'z' && buf[11] == '/' {
idx := bytes.IndexByte(buf, ' ')
if idx < 0 {
return unixPathToWinPath(buf[9:]), length
}
return unixPathToWinPath(buf[9:idx]), idx + 1
}
return "", 0
}

func unixPathToWinPath(buf []byte) string {
return fmt.Sprintf("%c:%s", buf[1], strings.ReplaceAll(string(buf[2:]), "/", "\\"))
}
7 changes: 7 additions & 0 deletions trzsz/pty_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ package trzsz

import (
"context"
"os"
"os/exec"
"strings"
"syscall"
"time"

"github.com/UserExistsError/conpty"
"golang.org/x/sys/windows"
Expand Down Expand Up @@ -147,6 +150,10 @@ func (t *TrzszPty) Close() {
}
t.cpty.Close()
resetVirtualTerminal(t.inMode, t.outMode)
time.Sleep(100 * time.Millisecond)
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
t.closed = true
}

Expand Down
Loading

0 comments on commit 38f3cb6

Please sign in to comment.