blob: 3a11b79bb3127ed1901aa63266bbc0fc062d11dd [file] [log] [blame]
Dan Willemsenb82471a2018-05-17 16:37:09 -07001// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package terminal
16
17import (
18 "bytes"
19 "io"
20 "os"
21 "syscall"
22 "unsafe"
23)
24
Colin Crossce525352019-06-08 18:58:00 -070025func isSmartTerminal(w io.Writer) bool {
Dan Willemsenb82471a2018-05-17 16:37:09 -070026 if f, ok := w.(*os.File); ok {
27 var termios syscall.Termios
28 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
29 ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
30 0, 0, 0)
31 return err == 0
Colin Crossdde49cb2019-06-08 21:59:42 -070032 } else if _, ok := w.(*fakeSmartTerminal); ok {
33 return true
Dan Willemsenb82471a2018-05-17 16:37:09 -070034 }
35 return false
36}
37
38func termWidth(w io.Writer) (int, bool) {
39 if f, ok := w.(*os.File); ok {
40 var winsize struct {
41 ws_row, ws_column uint16
42 ws_xpixel, ws_ypixel uint16
43 }
44 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
45 syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
46 0, 0, 0)
47 return int(winsize.ws_column), err == 0
Colin Crossdde49cb2019-06-08 21:59:42 -070048 } else if f, ok := w.(*fakeSmartTerminal); ok {
49 return f.termWidth, true
Dan Willemsenb82471a2018-05-17 16:37:09 -070050 }
51 return 0, false
52}
53
54// stripAnsiEscapes strips ANSI control codes from a byte array in place.
55func stripAnsiEscapes(input []byte) []byte {
56 // read represents the remaining part of input that needs to be processed.
57 read := input
58 // write represents where we should be writing in input.
59 // It will share the same backing store as input so that we make our modifications
60 // in place.
61 write := input
62
63 // advance will copy count bytes from read to write and advance those slices
64 advance := func(write, read []byte, count int) ([]byte, []byte) {
65 copy(write, read[:count])
66 return write[count:], read[count:]
67 }
68
69 for {
70 // Find the next escape sequence
71 i := bytes.IndexByte(read, 0x1b)
72 // If it isn't found, or if there isn't room for <ESC>[, finish
73 if i == -1 || i+1 >= len(read) {
74 copy(write, read)
75 break
76 }
77
78 // Not a CSI code, continue searching
79 if read[i+1] != '[' {
80 write, read = advance(write, read, i+1)
81 continue
82 }
83
84 // Found a CSI code, advance up to the <ESC>
85 write, read = advance(write, read, i)
86
87 // Find the end of the CSI code
88 i = bytes.IndexFunc(read, func(r rune) bool {
89 return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
90 })
91 if i == -1 {
92 // We didn't find the end of the code, just remove the rest
93 i = len(read) - 1
94 }
95
96 // Strip off the end marker too
97 i = i + 1
98
99 // Skip the reader forward and reduce final length by that amount
100 read = read[i:]
101 input = input[:len(input)-i]
102 }
103
104 return input
105}
Colin Crossdde49cb2019-06-08 21:59:42 -0700106
107type fakeSmartTerminal struct {
108 bytes.Buffer
109 termWidth int
110}