package commands import ( "errors" "log" "strconv" "golang.org/x/sys/unix" ) func Control(c rune) rune { return c & 0x1f } func sttyCharFromDesc(c string) (rune, error) { if len(c) > 2 || len(c) < 1 { return 'i', errors.New( "stty char description has unsupported length: " + strconv.Itoa(len(c)), ) } if rune(c[0]) == '^' { return Control(rune(c[1])), nil } return rune(c[0]), nil } const fakeUname = "Linux 5.16.19-76051619-generic" type CommandExecutor interface { Execute(shell *Shell, args []string) error } type SttyCommand struct{} func (c *SttyCommand) Execute(shell *Shell, args []string) error { // Tramp uses: // -inlcr: don't translate newline to carriage return // -onlcr: don't translate newline to carriage return-newline // -echo: echo input characters // kill CHAR: CHAR will erase the current line // erase CHAR: CHAR will erase the last character typed // icanon: enable special charactersq fd := int(shell.GetPty().Fd()) const ioctlReadTermios = unix.TCGETS // Linux const ioctlWriteTermios = unix.TCSETS // Linux termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) if err != nil { panic(err) } newState := *termios var skipNext bool for i, arg := range args { if skipNext { skipNext = false continue } var disable bool if arg[0] == '-' { disable = true arg = arg[1:] } switch arg { case "onlcr": if disable { newState.Iflag &^= unix.ONLCR continue } newState.Iflag |= unix.ONLCR case "inlcr": if disable { newState.Iflag &^= unix.INLCR continue } newState.Iflag |= unix.INLCR case "echo": if disable { newState.Lflag &^= unix.ECHO continue } newState.Lflag |= unix.ECHO case "kill": skipNext = true char, err := sttyCharFromDesc(args[i+1]) if err != nil { log.Printf( "Warning: Not applying unsupported character description for stty kill: %s", args[i+1], ) continue } newState.Cc[unix.VKILL] = uint8(char) case "erase": skipNext = true char, err := sttyCharFromDesc(args[i+1]) if err != nil { log.Printf( "Warning: Not applying unsupported character description for stty erase: %s", args[i+1], ) continue } newState.Cc[unix.VERASE] = uint8(char) case "icanon": if disable { newState.Lflag &^= unix.ICANON continue } newState.Lflag |= unix.ICANON case "tab0": newState.Oflag = (newState.Oflag & ^uint32(unix.TABDLY)) | unix.TAB0 case "iutf8": if disable { newState.Iflag &^= unix.IUTF8 continue } newState.Iflag |= unix.IUTF8 default: log.Printf( "Warning, tramp requested unexpected stty option %s", arg, ) } } if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil { return err } return nil }