Home | History | Annotate | Download | only in build
      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