blob: 642e213f7a2c193330093c3bce38e49955121e9e [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2012, 2013 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package utils
5
6import (
7 "bytes"
8 "compress/gzip"
9 "crypto/sha256"
10 "encoding/hex"
11 "io"
12 "io/ioutil"
13 "os"
14 "strings"
15 "unicode"
16)
17
18// TODO(ericsnow) Move the quoting helpers into the shell package?
19
20// ShQuote quotes s so that when read by bash, no metacharacters
21// within s will be interpreted as such.
22func ShQuote(s string) string {
23 // single-quote becomes single-quote, double-quote, single-quote, double-quote, single-quote
24 return `'` + strings.Replace(s, `'`, `'"'"'`, -1) + `'`
25}
26
27// WinPSQuote quotes s so that when read by powershell, no metacharacters
28// within s will be interpreted as such.
29func WinPSQuote(s string) string {
30 // See http://ss64.com/ps/syntax-esc.html#quotes.
31 // Double quotes inside single quotes are fine, double single quotes inside
32 // single quotes, not so much so. Having double quoted strings inside single
33 // quoted strings, ensure no expansion happens.
34 return `'` + strings.Replace(s, `'`, `"`, -1) + `'`
35}
36
37// WinCmdQuote quotes s so that when read by cmd.exe, no metacharacters
38// within s will be interpreted as such.
39func WinCmdQuote(s string) string {
40 // See http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx.
41 quoted := winCmdQuote(s)
42 return winCmdEscapeMeta(quoted)
43}
44
45func winCmdQuote(s string) string {
46 var escaped string
47 for _, c := range s {
48 switch c {
49 case '\\', '"':
50 escaped += `\`
51 }
52 escaped += string(c)
53 }
54 return `"` + escaped + `"`
55}
56
57func winCmdEscapeMeta(str string) string {
58 const meta = `()%!^"<>&|`
59 var newStr string
60 for _, c := range str {
61 if strings.Contains(meta, string(c)) {
62 newStr += "^"
63 }
64 newStr += string(c)
65 }
66 return newStr
67}
68
69// CommandString flattens a sequence of command arguments into a
70// string suitable for executing in a shell, escaping slashes,
71// variables and quotes as necessary; each argument is double-quoted
72// if and only if necessary.
73func CommandString(args ...string) string {
74 var buf bytes.Buffer
75 for i, arg := range args {
76 needsQuotes := false
77 var argBuf bytes.Buffer
78 for _, r := range arg {
79 if unicode.IsSpace(r) {
80 needsQuotes = true
81 } else if r == '"' || r == '$' || r == '\\' {
82 needsQuotes = true
83 argBuf.WriteByte('\\')
84 }
85 argBuf.WriteRune(r)
86 }
87 if i > 0 {
88 buf.WriteByte(' ')
89 }
90 if needsQuotes {
91 buf.WriteByte('"')
92 argBuf.WriteTo(&buf)
93 buf.WriteByte('"')
94 } else {
95 argBuf.WriteTo(&buf)
96 }
97 }
98 return buf.String()
99}
100
101// Gzip compresses the given data.
102func Gzip(data []byte) []byte {
103 var buf bytes.Buffer
104 w := gzip.NewWriter(&buf)
105 if _, err := w.Write(data); err != nil {
106 // Compression should never fail unless it fails
107 // to write to the underlying writer, which is a bytes.Buffer
108 // that never fails.
109 panic(err)
110 }
111 if err := w.Close(); err != nil {
112 panic(err)
113 }
114 return buf.Bytes()
115}
116
117// Gunzip uncompresses the given data.
118func Gunzip(data []byte) ([]byte, error) {
119 r, err := gzip.NewReader(bytes.NewReader(data))
120 if err != nil {
121 return nil, err
122 }
123 return ioutil.ReadAll(r)
124}
125
126// ReadSHA256 returns the SHA256 hash of the contents read from source
127// (hex encoded) and the size of the source in bytes.
128func ReadSHA256(source io.Reader) (string, int64, error) {
129 hash := sha256.New()
130 size, err := io.Copy(hash, source)
131 if err != nil {
132 return "", 0, err
133 }
134 digest := hex.EncodeToString(hash.Sum(nil))
135 return digest, size, nil
136}
137
138// ReadFileSHA256 is like ReadSHA256 but reads the contents of the
139// given file.
140func ReadFileSHA256(filename string) (string, int64, error) {
141 f, err := os.Open(filename)
142 if err != nil {
143 return "", 0, err
144 }
145 defer f.Close()
146 return ReadSHA256(f)
147}