• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
25func isTerminal(w io.Writer) bool {
26	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
32	}
33	return false
34}
35
36func termWidth(w io.Writer) (int, bool) {
37	if f, ok := w.(*os.File); ok {
38		var winsize struct {
39			ws_row, ws_column    uint16
40			ws_xpixel, ws_ypixel uint16
41		}
42		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
43			syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
44			0, 0, 0)
45		return int(winsize.ws_column), err == 0
46	}
47	return 0, false
48}
49
50// stripAnsiEscapes strips ANSI control codes from a byte array in place.
51func stripAnsiEscapes(input []byte) []byte {
52	// read represents the remaining part of input that needs to be processed.
53	read := input
54	// write represents where we should be writing in input.
55	// It will share the same backing store as input so that we make our modifications
56	// in place.
57	write := input
58
59	// advance will copy count bytes from read to write and advance those slices
60	advance := func(write, read []byte, count int) ([]byte, []byte) {
61		copy(write, read[:count])
62		return write[count:], read[count:]
63	}
64
65	for {
66		// Find the next escape sequence
67		i := bytes.IndexByte(read, 0x1b)
68		// If it isn't found, or if there isn't room for <ESC>[, finish
69		if i == -1 || i+1 >= len(read) {
70			copy(write, read)
71			break
72		}
73
74		// Not a CSI code, continue searching
75		if read[i+1] != '[' {
76			write, read = advance(write, read, i+1)
77			continue
78		}
79
80		// Found a CSI code, advance up to the <ESC>
81		write, read = advance(write, read, i)
82
83		// Find the end of the CSI code
84		i = bytes.IndexFunc(read, func(r rune) bool {
85			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
86		})
87		if i == -1 {
88			// We didn't find the end of the code, just remove the rest
89			i = len(read) - 1
90		}
91
92		// Strip off the end marker too
93		i = i + 1
94
95		// Skip the reader forward and reduce final length by that amount
96		read = read[i:]
97		input = input[:len(input)-i]
98	}
99
100	return input
101}
102