github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/termios/sgtty.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 //go:build !plan9 && !windows 6 // +build !plan9,!windows 7 8 package termios 9 10 import ( 11 "fmt" 12 "reflect" 13 "sort" 14 "strconv" 15 16 "golang.org/x/sys/unix" 17 ) 18 19 // GTTY returns the TTY struct for a given fd. It is like a New in 20 // many packages but the name GTTY is a tradition. 21 func GTTY(fd int) (*TTY, error) { 22 term, err := unix.IoctlGetTermios(fd, gets) 23 if err != nil { 24 return nil, err 25 } 26 w, err := unix.IoctlGetWinsize(fd, getWinSize) 27 if err != nil { 28 return nil, err 29 } 30 31 t := TTY{Opts: make(map[string]bool), CC: make(map[string]uint8)} 32 for n, b := range boolFields { 33 val := uint32(reflect.ValueOf(term).Elem().Field(b.word).Uint()) & b.mask 34 t.Opts[n] = val != 0 35 } 36 37 for n, c := range cc { 38 t.CC[n] = term.Cc[c] 39 } 40 41 // back in the day, you could have different i and o speeds. 42 // since about 1975, this has not been a thing. It's still in POSIX 43 // evidently. WTF? 44 t.Ispeed = int(term.Ispeed) 45 t.Ospeed = int(term.Ospeed) 46 t.Row = int(w.Row) 47 t.Col = int(w.Col) 48 49 return &t, nil 50 } 51 52 // STTY uses a TTY * to set TTY settings on an fd. 53 // It returns a new TTY struct for the fd after the changes are made, 54 // and an error. It does not change the original TTY struct. 55 func (t *TTY) STTY(fd int) (*TTY, error) { 56 // Get a unix.Termios which we can partially fill in. 57 term, err := unix.IoctlGetTermios(fd, gets) 58 if err != nil { 59 return nil, err 60 } 61 62 for n, b := range boolFields { 63 set := t.Opts[n] 64 i := reflect.ValueOf(term).Elem().Field(b.word).Uint() 65 if set { 66 i |= uint64(b.mask) 67 } else { 68 i &= ^uint64(b.mask) 69 } 70 reflect.ValueOf(term).Elem().Field(b.word).SetUint(i) 71 } 72 73 for n, c := range cc { 74 term.Cc[c] = t.CC[n] 75 } 76 77 term.Ispeed = speed(t.Ispeed) 78 term.Ospeed = speed(t.Ospeed) 79 80 if err := unix.IoctlSetTermios(fd, sets, term); err != nil { 81 return nil, err 82 } 83 84 w := &unix.Winsize{Row: uint16(t.Row), Col: uint16(t.Col)} 85 if err := unix.IoctlSetWinsize(fd, setWinSize, w); err != nil { 86 return nil, err 87 } 88 89 return GTTY(fd) 90 } 91 92 // String will stringify a TTY, including printing out the options all in the same order. 93 // The options are presented in the order: 94 // integer options as name:value 95 // boolean options which are set, printed as name, sorted by name 96 // boolean options which are clear, printed as ~name, sorted by name 97 // This ordering makes it a bit more readable: integer value, sorted set values, sorted clear values 98 func (t *TTY) String() string { 99 s := fmt.Sprintf("speed:%v ", t.Ispeed) 100 s += fmt.Sprintf("rows:%d cols:%d", t.Row, t.Col) 101 102 var intopts []string 103 for n, c := range t.CC { 104 intopts = append(intopts, fmt.Sprintf("%v:%#02x", n, c)) 105 } 106 sort.Strings(intopts) 107 108 var trueopts, falseopts []string 109 for n, set := range t.Opts { 110 if set { 111 trueopts = append(trueopts, n) 112 } else { 113 falseopts = append(falseopts, "~"+n) 114 } 115 } 116 sort.Strings(trueopts) 117 sort.Strings(falseopts) 118 119 for _, v := range append(intopts, append(trueopts, falseopts...)...) { 120 s += fmt.Sprintf(" %s", v) 121 } 122 return s 123 } 124 125 func intarg(s []string, bits int) (int, error) { 126 if len(s) < 2 { 127 return -1, fmt.Errorf("%s requires an arg", s[0]) 128 } 129 i, err := strconv.ParseUint(s[1], 0, bits) 130 if err != nil { 131 return -1, fmt.Errorf("%s is not a number", s) 132 } 133 return int(i), nil 134 } 135 136 // SetOpts sets opts in a TTY given an array of key-value pairs and 137 // booleans. The arguments are a variety of key-value pairs and booleans. 138 // booleans are cleared if the first char is a -, set otherwise. 139 func (t *TTY) SetOpts(opts []string) error { 140 var err error 141 for i := 0; i < len(opts) && err == nil; i++ { 142 o := opts[i] 143 switch o { 144 case "rows": 145 t.Row, err = intarg(opts[i:], 16) 146 i++ 147 continue 148 case "cols": 149 t.Col, err = intarg(opts[i:], 16) 150 i++ 151 continue 152 case "speed": 153 // 32 may sound crazy but ... baud can be REALLY large 154 t.Ispeed, err = intarg(opts[i:], 32) 155 i++ 156 continue 157 } 158 159 // see if it's one of the control char options. 160 if _, ok := cc[opts[i]]; ok { 161 var opt int 162 if opt, err = intarg(opts[i:], 8); err != nil { 163 return err 164 } 165 166 t.CC[opts[i]] = uint8(opt) 167 i++ 168 continue 169 } 170 171 // At this point, it has to be one of the boolean ones 172 // or we're done here. 173 set := true 174 if o[0] == '~' { 175 set = false 176 o = o[1:] 177 } 178 if _, ok := boolFields[o]; !ok { 179 return fmt.Errorf("opt %v is not valid", o) 180 } 181 t.Opts[o] = set 182 if err != nil { 183 return err 184 } 185 } 186 return err 187 } 188 189 // Raw sets a TTY into raw mode, returning a TTY struct 190 func Raw(fd int) (*TTY, error) { 191 t, err := GTTY(fd) 192 if err != nil { 193 return nil, err 194 } 195 196 t.SetOpts([]string{"~ignbrk", "~brkint", "~parmrk", "~istrip", "~inlcr", "~igncr", "~icrnl", "~ixon", "~opost", "~echo", "~echonl", "~icanon", "~isig", "~iexten", "~parenb" /*"cs8", */, "min", "1", "time", "0"}) 197 198 return t.STTY(fd) 199 }