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 15 package build 16 17 import ( 18 "bytes" 19 "io" 20 "os" 21 "path/filepath" 22 "strings" 23 "syscall" 24 "unsafe" 25 ) 26 27 // indexList finds the index of a string in a []string 28 func indexList(s string, list []string) int { 29 for i, l := range list { 30 if l == s { 31 return i 32 } 33 } 34 35 return -1 36 } 37 38 // inList determines whether a string is in a []string 39 func inList(s string, list []string) bool { 40 return indexList(s, list) != -1 41 } 42 43 // ensureDirectoriesExist is a shortcut to os.MkdirAll, sending errors to the ctx logger. 44 func ensureDirectoriesExist(ctx Context, dirs ...string) { 45 for _, dir := range dirs { 46 err := os.MkdirAll(dir, 0777) 47 if err != nil { 48 ctx.Fatalf("Error creating %s: %q\n", dir, err) 49 } 50 } 51 } 52 53 // ensureEmptyFileExists ensures that the containing directory exists, and the 54 // specified file exists. If it doesn't exist, it will write an empty file. 55 func ensureEmptyFileExists(ctx Context, file string) { 56 ensureDirectoriesExist(ctx, filepath.Dir(file)) 57 if _, err := os.Stat(file); os.IsNotExist(err) { 58 f, err := os.Create(file) 59 if err != nil { 60 ctx.Fatalf("Error creating %s: %q\n", file, err) 61 } 62 f.Close() 63 } else if err != nil { 64 ctx.Fatalf("Error checking %s: %q\n", file, err) 65 } 66 } 67 68 // singleUnquote is similar to strconv.Unquote, but can handle multi-character strings inside single quotes. 69 func singleUnquote(str string) (string, bool) { 70 if len(str) < 2 || str[0] != '\'' || str[len(str)-1] != '\'' { 71 return "", false 72 } 73 return str[1 : len(str)-1], true 74 } 75 76 // decodeKeyValue decodes a key=value string 77 func decodeKeyValue(str string) (string, string, bool) { 78 idx := strings.IndexRune(str, '=') 79 if idx == -1 { 80 return "", "", false 81 } 82 return str[:idx], str[idx+1:], true 83 } 84 85 func isTerminal(w io.Writer) bool { 86 if f, ok := w.(*os.File); ok { 87 var termios syscall.Termios 88 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(), 89 ioctlGetTermios, uintptr(unsafe.Pointer(&termios)), 90 0, 0, 0) 91 return err == 0 92 } 93 return false 94 } 95 96 func termWidth(w io.Writer) (int, bool) { 97 if f, ok := w.(*os.File); ok { 98 var winsize struct { 99 ws_row, ws_column uint16 100 ws_xpixel, ws_ypixel uint16 101 } 102 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(), 103 syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)), 104 0, 0, 0) 105 return int(winsize.ws_column), err == 0 106 } 107 return 0, false 108 } 109 110 // stripAnsiEscapes strips ANSI control codes from a byte array in place. 111 func stripAnsiEscapes(input []byte) []byte { 112 // read represents the remaining part of input that needs to be processed. 113 read := input 114 // write represents where we should be writing in input. 115 // It will share the same backing store as input so that we make our modifications 116 // in place. 117 write := input 118 119 // advance will copy count bytes from read to write and advance those slices 120 advance := func(write, read []byte, count int) ([]byte, []byte) { 121 copy(write, read[:count]) 122 return write[count:], read[count:] 123 } 124 125 for { 126 // Find the next escape sequence 127 i := bytes.IndexByte(read, 0x1b) 128 // If it isn't found, or if there isn't room for <ESC>[, finish 129 if i == -1 || i+1 >= len(read) { 130 copy(write, read) 131 break 132 } 133 134 // Not a CSI code, continue searching 135 if read[i+1] != '[' { 136 write, read = advance(write, read, i+1) 137 continue 138 } 139 140 // Found a CSI code, advance up to the <ESC> 141 write, read = advance(write, read, i) 142 143 // Find the end of the CSI code 144 i = bytes.IndexFunc(read, func(r rune) bool { 145 return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') 146 }) 147 if i == -1 { 148 // We didn't find the end of the code, just remove the rest 149 i = len(read) - 1 150 } 151 152 // Strip off the end marker too 153 i = i + 1 154 155 // Skip the reader forward and reduce final length by that amount 156 read = read[i:] 157 input = input[:len(input)-i] 158 } 159 160 return input 161 } 162