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