diff --git a/termenv_unix.go b/termenv_unix.go index 4f439c4..413fc6d 100644 --- a/termenv_unix.go +++ b/termenv_unix.go @@ -8,10 +8,16 @@ import ( "os" "strconv" "strings" + "time" "golang.org/x/sys/unix" ) +const ( + // timeout for OSC queries + OSCTimeout = 5 * time.Second +) + func colorProfile() Profile { term := os.Getenv("TERM") colorTerm := os.Getenv("COLORTERM") @@ -84,7 +90,34 @@ func backgroundColor() Color { return ANSIColor(0) } +func waitForData(fd uintptr, timeout time.Duration) error { + tv := unix.NsecToTimeval(int64(timeout)) + var readfds unix.FdSet + readfds.Set(int(fd)) + + for { + n, err := unix.Select(int(fd)+1, &readfds, nil, nil, &tv) + if err == unix.EINTR { + continue + } + if err != nil { + return err + } + if n == 0 { + return fmt.Errorf("timeout") + } + + break + } + + return nil +} + func readNextByte(f *os.File) (byte, error) { + if err := waitForData(f.Fd(), OSCTimeout); err != nil { + return 0, err + } + var b [1]byte n, err := f.Read(b[:]) if err != nil { @@ -107,20 +140,14 @@ func readNextResponse(fd *os.File) (response string, isOSC bool, err error) { return "", false, err } - // if we encounter a backslash, this is a left-over from the previous OSC - // response, which can be terminated by an optional backslash - if start == '\\' { + // first byte must be ESC + for start != '\033' { start, err = readNextByte(fd) if err != nil { return "", false, err } } - // first byte must be ESC - if start != '\033' { - return "", false, ErrStatusReport - } - response += string(start) // next byte is either '[' (cursor position response) or ']' (OSC response)