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