github.com/olljanat/moby@v1.13.1/pkg/term/term.go (about)

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