term_unix.go raw

   1  // Copyright 2019 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
   6  
   7  package term
   8  
   9  import (
  10  	"golang.org/x/sys/unix"
  11  )
  12  
  13  type state struct {
  14  	termios unix.Termios
  15  }
  16  
  17  func isTerminal(fd int) bool {
  18  	_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
  19  	return err == nil
  20  }
  21  
  22  func makeRaw(fd int) (*State, error) {
  23  	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
  24  	if err != nil {
  25  		return nil, err
  26  	}
  27  
  28  	oldState := State{state{termios: *termios}}
  29  
  30  	// This attempts to replicate the behaviour documented for cfmakeraw in
  31  	// the termios(3) manpage.
  32  	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
  33  	termios.Oflag &^= unix.OPOST
  34  	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
  35  	termios.Cflag &^= unix.CSIZE | unix.PARENB
  36  	termios.Cflag |= unix.CS8
  37  	termios.Cc[unix.VMIN] = 1
  38  	termios.Cc[unix.VTIME] = 0
  39  	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
  40  		return nil, err
  41  	}
  42  
  43  	return &oldState, nil
  44  }
  45  
  46  func getState(fd int) (*State, error) {
  47  	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
  48  	if err != nil {
  49  		return nil, err
  50  	}
  51  
  52  	return &State{state{termios: *termios}}, nil
  53  }
  54  
  55  func restore(fd int, state *State) error {
  56  	return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
  57  }
  58  
  59  func getSize(fd int) (width, height int, err error) {
  60  	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
  61  	if err != nil {
  62  		return 0, 0, err
  63  	}
  64  	return int(ws.Col), int(ws.Row), nil
  65  }
  66  
  67  // passwordReader is an io.Reader that reads from a specific file descriptor.
  68  type passwordReader int
  69  
  70  func (r passwordReader) Read(buf []byte) (int, error) {
  71  	return unix.Read(int(r), buf)
  72  }
  73  
  74  func readPassword(fd int) ([]byte, error) {
  75  	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
  76  	if err != nil {
  77  		return nil, err
  78  	}
  79  
  80  	newState := *termios
  81  	newState.Lflag &^= unix.ECHO
  82  	newState.Lflag |= unix.ICANON | unix.ISIG
  83  	newState.Iflag |= unix.ICRNL
  84  	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
  85  		return nil, err
  86  	}
  87  
  88  	defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
  89  
  90  	return readPasswordLine(passwordReader(fd))
  91  }
  92