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