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