Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
2.3 KiB
102 lines
2.3 KiB
package shellquote |
|
|
|
import ( |
|
"bytes" |
|
"strings" |
|
"unicode/utf8" |
|
) |
|
|
|
// Join quotes each argument and joins them with a space. |
|
// If passed to /bin/sh, the resulting string will be split back into the |
|
// original arguments. |
|
func Join(args ...string) string { |
|
var buf bytes.Buffer |
|
for i, arg := range args { |
|
if i != 0 { |
|
buf.WriteByte(' ') |
|
} |
|
quote(arg, &buf) |
|
} |
|
return buf.String() |
|
} |
|
|
|
const ( |
|
specialChars = "\\'\"`${[|&;<>()*?!" |
|
extraSpecialChars = " \t\n" |
|
prefixChars = "~" |
|
) |
|
|
|
func quote(word string, buf *bytes.Buffer) { |
|
// We want to try to produce a "nice" output. As such, we will |
|
// backslash-escape most characters, but if we encounter a space, or if we |
|
// encounter an extra-special char (which doesn't work with |
|
// backslash-escaping) we switch over to quoting the whole word. We do this |
|
// with a space because it's typically easier for people to read multi-word |
|
// arguments when quoted with a space rather than with ugly backslashes |
|
// everywhere. |
|
origLen := buf.Len() |
|
|
|
if len(word) == 0 { |
|
// oops, no content |
|
buf.WriteString("''") |
|
return |
|
} |
|
|
|
cur, prev := word, word |
|
atStart := true |
|
for len(cur) > 0 { |
|
c, l := utf8.DecodeRuneInString(cur) |
|
cur = cur[l:] |
|
if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) { |
|
// copy the non-special chars up to this point |
|
if len(cur) < len(prev) { |
|
buf.WriteString(prev[0 : len(prev)-len(cur)-l]) |
|
} |
|
buf.WriteByte('\\') |
|
buf.WriteRune(c) |
|
prev = cur |
|
} else if strings.ContainsRune(extraSpecialChars, c) { |
|
// start over in quote mode |
|
buf.Truncate(origLen) |
|
goto quote |
|
} |
|
atStart = false |
|
} |
|
if len(prev) > 0 { |
|
buf.WriteString(prev) |
|
} |
|
return |
|
|
|
quote: |
|
// quote mode |
|
// Use single-quotes, but if we find a single-quote in the word, we need |
|
// to terminate the string, emit an escaped quote, and start the string up |
|
// again |
|
inQuote := false |
|
for len(word) > 0 { |
|
i := strings.IndexRune(word, '\'') |
|
if i == -1 { |
|
break |
|
} |
|
if i > 0 { |
|
if !inQuote { |
|
buf.WriteByte('\'') |
|
inQuote = true |
|
} |
|
buf.WriteString(word[0:i]) |
|
} |
|
word = word[i+1:] |
|
if inQuote { |
|
buf.WriteByte('\'') |
|
inQuote = false |
|
} |
|
buf.WriteString("\\'") |
|
} |
|
if len(word) > 0 { |
|
if !inQuote { |
|
buf.WriteByte('\'') |
|
} |
|
buf.WriteString(word) |
|
buf.WriteByte('\'') |
|
} |
|
}
|
|
|