github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/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  	"unsafe"
    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  }
    31  
    32  // StdStreams returns the standard streams (stdin, stdout, stedrr).
    33  func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
    34  	return os.Stdin, os.Stdout, os.Stderr
    35  }
    36  
    37  // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
    38  func GetFdInfo(in interface{}) (uintptr, bool) {
    39  	var inFd uintptr
    40  	var isTerminalIn bool
    41  	if file, ok := in.(*os.File); ok {
    42  		inFd = file.Fd()
    43  		isTerminalIn = IsTerminal(inFd)
    44  	}
    45  	return inFd, isTerminalIn
    46  }
    47  
    48  // GetWinsize returns the window size based on the specified file descriptor.
    49  func GetWinsize(fd uintptr) (*Winsize, error) {
    50  	ws := &Winsize{}
    51  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
    52  	// Skip errno = 0
    53  	if err == 0 {
    54  		return ws, nil
    55  	}
    56  	return ws, err
    57  }
    58  
    59  // SetWinsize tries to set the specified window size for the specified file descriptor.
    60  func SetWinsize(fd uintptr, ws *Winsize) error {
    61  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
    62  	// Skip errno = 0
    63  	if err == 0 {
    64  		return nil
    65  	}
    66  	return err
    67  }
    68  
    69  // IsTerminal returns true if the given file descriptor is a terminal.
    70  func IsTerminal(fd uintptr) bool {
    71  	var termios Termios
    72  	return tcget(fd, &termios) == 0
    73  }
    74  
    75  // RestoreTerminal restores the terminal connected to the given file descriptor
    76  // to a previous state.
    77  func RestoreTerminal(fd uintptr, state *State) error {
    78  	if state == nil {
    79  		return ErrInvalidState
    80  	}
    81  	if err := tcset(fd, &state.termios); err != 0 {
    82  		return err
    83  	}
    84  	return nil
    85  }
    86  
    87  // SaveState saves the state of the terminal connected to the given file descriptor.
    88  func SaveState(fd uintptr) (*State, error) {
    89  	var oldState State
    90  	if err := tcget(fd, &oldState.termios); err != 0 {
    91  		return nil, err
    92  	}
    93  
    94  	return &oldState, nil
    95  }
    96  
    97  // DisableEcho applies the specified state to the terminal connected to the file
    98  // descriptor, with echo disabled.
    99  func DisableEcho(fd uintptr, state *State) error {
   100  	newState := state.termios
   101  	newState.Lflag &^= syscall.ECHO
   102  
   103  	if err := tcset(fd, &newState); err != 0 {
   104  		return err
   105  	}
   106  	handleInterrupt(fd, state)
   107  	return nil
   108  }
   109  
   110  // SetRawTerminal puts the terminal connected to the given file descriptor into
   111  // raw mode and returns the previous state.
   112  func SetRawTerminal(fd uintptr) (*State, error) {
   113  	oldState, err := MakeRaw(fd)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	handleInterrupt(fd, oldState)
   118  	return oldState, err
   119  }
   120  
   121  func handleInterrupt(fd uintptr, state *State) {
   122  	sigchan := make(chan os.Signal, 1)
   123  	signal.Notify(sigchan, os.Interrupt)
   124  
   125  	go func() {
   126  		_ = <-sigchan
   127  		RestoreTerminal(fd, state)
   128  	}()
   129  }