github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/stty/termios.go (about)

     1  // Copyright 2015-2017 the u-root 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  package main
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"log"
    11  	"reflect"
    12  	"strconv"
    13  
    14  	"golang.org/x/sys/unix"
    15  )
    16  
    17  type (
    18  	// tty is an os-independent version of the combined info in termios and window size structs.
    19  	// It is used to get/set info to the termios functions as well as marshal/unmarshal data
    20  	// in JSON formwt for dump and loading.
    21  	tty struct {
    22  		Ispeed int
    23  		Ospeed int
    24  		Row    int
    25  		Col    int
    26  
    27  		CC map[string]uint8
    28  
    29  		Opts map[string]bool
    30  	}
    31  )
    32  
    33  func gtty(fd int) (*tty, error) {
    34  	term, err := unix.IoctlGetTermios(fd, unix.TCGETS)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	w, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	var t = tty{Opts: make(map[string]bool), CC: make(map[string]uint8)}
    44  	for n, b := range boolFields {
    45  		val := uint32(reflect.ValueOf(term).Elem().Field(b.word).Uint()) & b.mask
    46  		t.Opts[n] = val != 0
    47  	}
    48  
    49  	for n, c := range cc {
    50  		t.CC[n] = term.Cc[c]
    51  	}
    52  
    53  	// back in the day, you could have different i and o speeds.
    54  	// since about 1975, this has not been a thing. It's still in POSIX
    55  	// evidently. WTF?
    56  	t.Ispeed = int(term.Ispeed)
    57  	t.Ospeed = int(term.Ospeed)
    58  	t.Row = int(w.Row)
    59  	t.Col = int(w.Col)
    60  
    61  	return &t, nil
    62  }
    63  
    64  func stty(fd int, t *tty) (*tty, error) {
    65  	// Get a unix.Termios which we can partially fill in.
    66  	term, err := unix.IoctlGetTermios(fd, unix.TCGETS)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	for n, b := range boolFields {
    72  		set := t.Opts[n]
    73  		i := reflect.ValueOf(term).Elem().Field(b.word).Uint()
    74  		if set {
    75  			i |= uint64(b.mask)
    76  		} else {
    77  			i &= ^uint64(b.mask)
    78  		}
    79  		reflect.ValueOf(term).Elem().Field(b.word).SetUint(i)
    80  	}
    81  
    82  	for n, c := range cc {
    83  		term.Cc[c] = t.CC[n]
    84  	}
    85  
    86  	term.Ispeed = uint32(t.Ispeed)
    87  	term.Ospeed = uint32(t.Ospeed)
    88  
    89  	if err := unix.IoctlSetTermios(fd, unix.TCSETS, term); err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	w := &unix.Winsize{Row: uint16(t.Row), Col: uint16(t.Col)}
    94  	if err := unix.IoctlSetWinsize(fd, unix.TIOCSWINSZ, w); err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	return gtty(fd)
    99  }
   100  
   101  func pretty(w io.Writer, t *tty) {
   102  	fmt.Printf("speed: %v ", t.Ispeed)
   103  	for n, c := range t.CC {
   104  		fmt.Printf("%v: %#q, ", n, c)
   105  	}
   106  	fmt.Fprintf(w, "%d rows, %d cols\n", t.Row, t.Col)
   107  
   108  	for n, set := range t.Opts {
   109  		if set {
   110  			fmt.Fprintf(w, "%v ", n)
   111  		} else {
   112  			fmt.Fprintf(w, "~%v ", n)
   113  		}
   114  	}
   115  	fmt.Fprintln(w)
   116  }
   117  
   118  func intarg(s []string) int {
   119  	if len(s) < 2 {
   120  		log.Fatalf("%s requires an arg", s[0])
   121  	}
   122  	i, err := strconv.Atoi(s[1])
   123  	if err != nil {
   124  		log.Fatalf("%s is not a number", s)
   125  	}
   126  	return i
   127  }
   128  
   129  // the arguments are a variety of key-value pairs and booleans.
   130  // booleans are cleared if the first char is a -, set otherwise.
   131  func setOpts(t *tty, opts []string) error {
   132  	for i := 0; i < len(opts); i++ {
   133  		o := opts[i]
   134  		switch o {
   135  		case "row":
   136  			t.Row = intarg(opts[i:])
   137  			i++
   138  			continue
   139  		case "col":
   140  			t.Col = intarg(opts[i:])
   141  			i++
   142  			continue
   143  		case "speed":
   144  			t.Ispeed = intarg(opts[i:])
   145  			i++
   146  			continue
   147  		}
   148  
   149  		// see if it's one of the control char options.
   150  		if _, ok := cc[opts[i]]; ok {
   151  			t.CC[opts[i]] = uint8(intarg(opts[i:]))
   152  			i++
   153  			continue
   154  		}
   155  
   156  		// At this point, it has to be one of the boolean ones
   157  		// or we're done here.
   158  		set := true
   159  		if o[0] == '~' {
   160  			set = false
   161  			o = o[1:]
   162  		}
   163  		if _, ok := boolFields[o]; !ok {
   164  			log.Fatalf("%s: unknown option", o)
   165  		}
   166  
   167  		t.Opts[o] = set
   168  	}
   169  	return nil
   170  }
   171  
   172  func setRaw(fd int) (*tty, error) {
   173  	t, err := gtty(fd)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	setOpts(t, []string{"~ignbrk", "~brkint", "~parmrk", "~istrip", "~inlcr", "~igncr", "~icrnl", "~ixon", "~opost", "~echo", "~echonl", "~icanon", "~isig", "~iexten", "~parenb" /*"cs8", */, "min", "1", "time", "0"})
   179  
   180  	return stty(fd, t)
   181  }