899168eb50
In a future commit, we'll be adding some additional shell-related handling which logically belongs to the subprocess package. To avoid an import loop, move the ShellQuote function and its required variable from the tools package to the subprocess package. Update the only caller of this function. This commit contains no functional change.
123 lines
2.8 KiB
Go
123 lines
2.8 KiB
Go
package tools
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// quoteFieldRe greedily matches between matching pairs of '', "", or
|
|
// non-word characters.
|
|
quoteFieldRe = regexp.MustCompile("'(.*)'|\"(.*)\"|(\\S*)")
|
|
)
|
|
|
|
// QuotedFields is an alternative to strings.Fields (see:
|
|
// https://golang.org/pkg/strings#Fields) that respects spaces between matching
|
|
// pairs of quotation delimeters.
|
|
//
|
|
// For instance, the quoted fields of the string "foo bar 'baz etc'" would be:
|
|
// []string{"foo", "bar", "baz etc"}
|
|
//
|
|
// Whereas the same argument given to strings.Fields, would return:
|
|
// []string{"foo", "bar", "'baz", "etc'"}
|
|
func QuotedFields(s string) []string {
|
|
submatches := quoteFieldRe.FindAllStringSubmatch(s, -1)
|
|
out := make([]string, 0, len(submatches))
|
|
|
|
for _, matches := range submatches {
|
|
// if a leading or trailing space is found, ignore that
|
|
if matches[0] == "" {
|
|
continue
|
|
}
|
|
|
|
// otherwise, find the first non-empty match (inside balanced
|
|
// quotes, or a space-delimited string)
|
|
var str string
|
|
for _, m := range matches[1:] {
|
|
if len(m) > 0 {
|
|
str = m
|
|
break
|
|
}
|
|
}
|
|
|
|
out = append(out, str)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// Ljust returns a copied string slice where each element is left justified to
|
|
// match the width of the longest element in the set.
|
|
func Ljust(strs []string) []string {
|
|
llen := len(Longest(strs))
|
|
|
|
dup := make([]string, len(strs), cap(strs))
|
|
copy(dup, strs)
|
|
|
|
for i, str := range strs {
|
|
width := MaxInt(0, llen-len(str))
|
|
padding := strings.Repeat(" ", width)
|
|
|
|
dup[i] = str + padding
|
|
}
|
|
|
|
return dup
|
|
}
|
|
|
|
// Rjust returns a copied string slice where each element is right justified to
|
|
// match the width of the longest element in the set.
|
|
func Rjust(strs []string) []string {
|
|
llen := len(Longest(strs))
|
|
|
|
dup := make([]string, len(strs), cap(strs))
|
|
copy(dup, strs)
|
|
|
|
for i, str := range strs {
|
|
width := MaxInt(0, llen-len(str))
|
|
padding := strings.Repeat(" ", width)
|
|
|
|
dup[i] = padding + str
|
|
}
|
|
|
|
return dup
|
|
}
|
|
|
|
// Longest returns the longest element in the string slice in O(n) time and O(1)
|
|
// space. If strs is empty or nil, an empty string will be returned.
|
|
func Longest(strs []string) string {
|
|
if len(strs) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var longest string
|
|
var llen int
|
|
for _, str := range strs {
|
|
if len(str) >= llen {
|
|
longest = str
|
|
llen = len(longest)
|
|
}
|
|
}
|
|
|
|
return longest
|
|
}
|
|
|
|
// Indent returns a string which prepends "\t" TAB characters to the beginning
|
|
// of each line in the given string "str".
|
|
func Indent(str string) string {
|
|
indented := strings.Replace(str, "\n", "\n\t", -1)
|
|
if len(indented) > 0 {
|
|
indented = "\t" + indented
|
|
}
|
|
|
|
return indented
|
|
}
|
|
|
|
var (
|
|
tabRe = regexp.MustCompile(`(?m)^[ \t]+`)
|
|
)
|
|
|
|
// Undent removes all leading tabs in the given string "str", line-wise.
|
|
func Undent(str string) string {
|
|
return tabRe.ReplaceAllString(str, "")
|
|
}
|