github.com/portworx/docker@v1.12.1/pkg/term/term.go (about)

     1  // +build !windows
     2  
     3  // Package term provides provides structures and helper functions to work with
     4  // terminal (state, sizes).
     5  package term
     6  
     7  import (
     8  	"errors"
     9  	"io"
    10  	"os"
    11  	"os/signal"
    12  	"syscall"
    13  )
    14  
    15  var (
    16  	// ErrInvalidState is returned if the state of the terminal is invalid.
    17  	ErrInvalidState = errors.New("Invalid terminal state")
    18  )
    19  
    20  // State represents the state of the terminal.
    21  type State struct {
    22  	termios Termios
    23  }
    24  
    25  // Winsize represents the size of the terminal window.
    26  type Winsize struct {
    27  	Height uint16
    28  	Width  uint16
    29  	x      uint16
    30  	y      uint16
    31  }
    32  
    33  // StdStreams returns the standard streams (stdin, stdout, stedrr).
    34  func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
    35  	return os.Stdin, os.Stdout, os.Stderr
    36  }
    37  
    38  // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
    39  func GetFdInfo(in interface{}) (uintptr, bool) {
    40  	var inFd uintptr
    41  	var isTerminalIn bool
    42  	if file, ok := in.(*os.File); ok {
    43  		inFd = file.Fd()
    44  		isTerminalIn = IsTerminal(inFd)
    45  	}
    46  	return inFd, isTerminalIn
    47  }
    48  
    49  // IsTerminal returns true if the given file descriptor is a terminal.
    50  func IsTerminal(fd uintptr) bool {
    51  	var termios Termios
    52  	return tcget(fd, &termios) == 0
    53  }
    54  
    55  // RestoreTerminal restores the terminal connected to the given file descriptor
    56  // to a previous state.
    57  func RestoreTerminal(fd uintptr, state *State) error {
    58  	if state == nil {
    59  		return ErrInvalidState
    60  	}
    61  	if err := tcset(fd, &state.termios); err != 0 {
    62  		return err
    63  	}
    64  	return nil
    65  }
    66  
    67  // SaveState saves the state of the terminal connected to the given file descriptor.
    68  func SaveState(fd uintptr) (*State, error) {
    69  	var oldState State
    70  	if err := tcget(fd, &oldState.termios); err != 0 {
    71  		return nil, err
    72  	}
    73  
    74  	return &oldState, nil
    75  }
    76  
    77  // DisableEcho applies the specified state to the terminal connected to the file
    78  // descriptor, with echo disabled.
    79  func DisableEcho(fd uintptr, state *State) error {
    80  	newState := state.termios
    81  	newState.Lflag &^= syscall.ECHO
    82  
    83  	if err := tcset(fd, &newState); err != 0 {
    84  		return err
    85  	}
    86  	handleInterrupt(fd, state)
    87  	return nil
    88  }
    89  
    90  // SetRawTerminal puts the terminal connected to the given file descriptor into
    91  // raw mode and returns the previous state. On UNIX, this puts both the input
    92  // and output into raw mode. On Windows, it only puts the input into raw mode.
    93  func SetRawTerminal(fd uintptr) (*State, error) {
    94  	oldState, err := MakeRaw(fd)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	handleInterrupt(fd, oldState)
    99  	return oldState, err
   100  }
   101  
   102  // SetRawTerminalOutput puts the output of terminal connected to the given file
   103  // descriptor into raw mode. On UNIX, this does nothing and returns nil for the
   104  // state. On Windows, it disables LF -> CRLF translation.
   105  func SetRawTerminalOutput(fd uintptr) (*State, error) {
   106  	return nil, nil
   107  }
   108  
   109  func handleInterrupt(fd uintptr, state *State) {
   110  	sigchan := make(chan os.Signal, 1)
   111  	signal.Notify(sigchan, os.Interrupt)
   112  
   113  	go func() {
   114  		_ = <-sigchan
   115  		RestoreTerminal(fd, state)
   116  	}()
   117  }