blob: f698ccd0b38471d8beba87ef949fe6a4e5a28632 [file] [log] [blame]
Dan Willemsen1e704462016-08-21 15:17:17 -07001// 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 build
16
17import (
Dan Willemsen29f88272017-02-18 18:12:41 -080018 "bytes"
19 "io"
Dan Willemsen1e704462016-08-21 15:17:17 -070020 "os"
21 "path/filepath"
22 "strings"
Dan Willemsen29f88272017-02-18 18:12:41 -080023 "syscall"
24 "unsafe"
Dan Willemsen1e704462016-08-21 15:17:17 -070025)
26
Dan Willemsend9e8f0a2017-10-30 13:42:06 -070027func 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
Dan Willemsen1e704462016-08-21 15:17:17 -070035// indexList finds the index of a string in a []string
36func 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
47func 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.
52func 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
Jeff Gastonefc1b412017-03-29 17:29:06 -070061// ensureEmptyDirectoriesExist ensures that the given directories exist and are empty
62func 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
Dan Willemsen1e704462016-08-21 15:17:17 -070074// ensureEmptyFileExists ensures that the containing directory exists, and the
75// specified file exists. If it doesn't exist, it will write an empty file.
76func 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.
90func 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
98func 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}
Dan Willemsen29f88272017-02-18 18:12:41 -0800105
106func 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
117func 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.
132func 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}