parent
8b9ca2cf08
commit
a7853c1177
@ -0,0 +1,384 @@
|
||||
package quotearg
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
QAElideNullBytes = 0x01
|
||||
QAElideOuterQuotes = 0x02
|
||||
QASplitTrigraphs = 0x04
|
||||
)
|
||||
|
||||
const SizeMax = ^uint(0)
|
||||
|
||||
type QuotingOptions struct {
|
||||
Flags int
|
||||
QuoteTheseToo uint
|
||||
LeftQuote rune
|
||||
RightQuote rune
|
||||
Style QuotingStyle
|
||||
}
|
||||
|
||||
type QuotingStyle int
|
||||
|
||||
const (
|
||||
LiteralQuotingStyle QuotingStyle = iota
|
||||
ShellQuotingStyle
|
||||
ShellAlwaysQuotingStyle
|
||||
ShellEscapeQuotingStyle
|
||||
ShellEscapeAlwaysQuotingStyle
|
||||
CQuotingStyle
|
||||
CMaybeQuotingStyle
|
||||
EscapeQuotingStyle
|
||||
LocaleQuotingStyle
|
||||
CLocaleQuotingStyle
|
||||
CustomQuotingStyle
|
||||
)
|
||||
|
||||
var QuotingStyleArgs = []string{
|
||||
"literal",
|
||||
"shell",
|
||||
"shell-always",
|
||||
"shell-escape",
|
||||
"shell-escape-always",
|
||||
"c",
|
||||
"c-maybe",
|
||||
"escape",
|
||||
"locale",
|
||||
"clocale",
|
||||
"",
|
||||
}
|
||||
|
||||
func GetTextQuote(s QuotingStyle) rune {
|
||||
if s == CLocaleQuotingStyle {
|
||||
return '\''
|
||||
} else {
|
||||
return '"'
|
||||
}
|
||||
}
|
||||
|
||||
func QuoteargBufferRestyled(
|
||||
buffer []rune,
|
||||
arg []rune,
|
||||
style QuotingStyle,
|
||||
flags int,
|
||||
quoteTheseToo uint,
|
||||
leftQuote rune,
|
||||
rightQuote rune,
|
||||
) {
|
||||
var pos int
|
||||
var elideOuterQuotes bool = (flags & QAElideOuterQuotes) != 0
|
||||
var pendingShellEscapeEnd bool
|
||||
var escaping bool
|
||||
var quoteRune rune
|
||||
var backslashEscapes bool
|
||||
var allCAndShellQuoteCompat bool = true
|
||||
|
||||
store := func(c rune) {
|
||||
buffer[pos] = c
|
||||
pos++
|
||||
}
|
||||
|
||||
startESC := func() {
|
||||
if elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
escaping = true
|
||||
if style == ShellAlwaysQuotingStyle && !pendingShellEscapeEnd {
|
||||
store('\'')
|
||||
store('$')
|
||||
store('\'')
|
||||
pendingShellEscapeEnd = true
|
||||
}
|
||||
store('\\')
|
||||
}
|
||||
|
||||
endESC := func() {
|
||||
if pendingShellEscapeEnd && !escaping {
|
||||
store('\'')
|
||||
store('\'')
|
||||
pendingShellEscapeEnd = false
|
||||
}
|
||||
}
|
||||
|
||||
switch style {
|
||||
case CMaybeQuotingStyle:
|
||||
style = CQuotingStyle
|
||||
elideOuterQuotes = true
|
||||
fallthrough
|
||||
case CQuotingStyle:
|
||||
if !elideOuterQuotes {
|
||||
store('"')
|
||||
}
|
||||
backslashEscapes = true
|
||||
quoteRune = '"'
|
||||
break
|
||||
case EscapeQuotingStyle:
|
||||
backslashEscapes = true
|
||||
elideOuterQuotes = false
|
||||
break
|
||||
case LocaleQuotingStyle:
|
||||
fallthrough
|
||||
case CLocaleQuotingStyle:
|
||||
fallthrough
|
||||
case CustomQuotingStyle:
|
||||
if style != CustomQuotingStyle {
|
||||
leftQuote = GetTextQuote(style)
|
||||
rightQuote = GetTextQuote(style)
|
||||
}
|
||||
if !elideOuterQuotes {
|
||||
// todo figure out the voodoo
|
||||
}
|
||||
backslashEscapes = true
|
||||
quoteRune = rightQuote
|
||||
break
|
||||
case ShellEscapeQuotingStyle:
|
||||
backslashEscapes = true
|
||||
fallthrough
|
||||
case ShellQuotingStyle:
|
||||
elideOuterQuotes = true
|
||||
fallthrough
|
||||
case ShellEscapeAlwaysQuotingStyle:
|
||||
if !elideOuterQuotes {
|
||||
backslashEscapes = true
|
||||
}
|
||||
fallthrough
|
||||
case ShellAlwaysQuotingStyle:
|
||||
style = ShellAlwaysQuotingStyle
|
||||
if !elideOuterQuotes {
|
||||
store('\'')
|
||||
}
|
||||
quoteRune = '\''
|
||||
break
|
||||
default:
|
||||
panic("invalid style")
|
||||
}
|
||||
|
||||
for i := 0; i < len(arg); i++ {
|
||||
var esc rune
|
||||
var isRightQuote bool
|
||||
var cAndShellQuoteCompat bool
|
||||
|
||||
quoteIsNext := arg[i+1] == quoteRune
|
||||
|
||||
if backslashEscapes &&
|
||||
style != ShellAlwaysQuotingStyle &&
|
||||
quoteIsNext {
|
||||
if elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
isRightQuote = true
|
||||
}
|
||||
|
||||
c := arg[i]
|
||||
|
||||
switch c {
|
||||
case 0x00:
|
||||
if backslashEscapes {
|
||||
startESC()
|
||||
if style != ShellQuotingStyle &&
|
||||
i+1 < len(arg) &&
|
||||
'0' <= arg[i+1] &&
|
||||
arg[i+1] <= '9' {
|
||||
store('0')
|
||||
store('0')
|
||||
}
|
||||
c = '0'
|
||||
} else if flags&QAElideNullBytes != 0 {
|
||||
continue
|
||||
}
|
||||
break
|
||||
case '?':
|
||||
switch style {
|
||||
case ShellAlwaysQuotingStyle:
|
||||
if elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
break
|
||||
case CQuotingStyle:
|
||||
if flags&QASplitTrigraphs != 0 &&
|
||||
i+2 < len(arg) &&
|
||||
arg[i+1] == '?' {
|
||||
switch arg[i+2] {
|
||||
case '!', '\'', '(', ')', '-', '/', '<', '=', '>':
|
||||
if elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
|
||||
c = arg[i+2]
|
||||
i += 2
|
||||
store('?')
|
||||
store('"')
|
||||
store('"')
|
||||
store('?')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
break
|
||||
case '\a':
|
||||
esc = 'a'
|
||||
goto CEscape
|
||||
case '\b':
|
||||
esc = 'b'
|
||||
goto CEscape
|
||||
case '\f':
|
||||
esc = 'f'
|
||||
goto CEscape
|
||||
case '\n':
|
||||
esc = 'n'
|
||||
goto CAndShellEscape
|
||||
case '\r':
|
||||
esc = 'r'
|
||||
goto CAndShellEscape
|
||||
case '\t':
|
||||
esc = 't'
|
||||
goto CAndShellEscape
|
||||
case '\v':
|
||||
esc = 'v'
|
||||
goto CEscape
|
||||
case '\\':
|
||||
esc = c
|
||||
if style == ShellAlwaysQuotingStyle {
|
||||
if elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
goto StoreC
|
||||
}
|
||||
|
||||
if backslashEscapes && elideOuterQuotes && quoteRune > 0 {
|
||||
goto StoreC
|
||||
}
|
||||
|
||||
CAndShellEscape:
|
||||
if style == ShellAlwaysQuotingStyle && elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
|
||||
CEscape:
|
||||
if backslashEscapes {
|
||||
c = esc
|
||||
goto StoreEscape
|
||||
}
|
||||
break
|
||||
|
||||
case '{', '}':
|
||||
if len(arg) != 1 {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case '#', '~':
|
||||
if i != 0 {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case ' ':
|
||||
cAndShellQuoteCompat = true
|
||||
fallthrough
|
||||
case '!', '"', '$', '&', '(', ')', '*', ';', '<', '=', '>', '[':
|
||||
fallthrough
|
||||
case '^', '`', '|':
|
||||
if style == ShellAlwaysQuotingStyle && elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
break
|
||||
case '\'':
|
||||
encounteredSingleQuote = true
|
||||
cAndShellQuoteCompat = true
|
||||
if style == ShellAlwaysQuotingStyle {
|
||||
if elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
// buffersize voodoo
|
||||
|
||||
store('\'')
|
||||
store('\\')
|
||||
store('\'')
|
||||
pendingShellEscapeEnd = false
|
||||
}
|
||||
break
|
||||
case '%', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5':
|
||||
fallthrough
|
||||
case '6', '7', '8', '9', ':', 'A', 'B', 'C', 'D', 'E', 'F':
|
||||
fallthrough
|
||||
case 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R':
|
||||
fallthrough
|
||||
case 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ']', '_', 'a', 'b':
|
||||
fallthrough
|
||||
case 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n':
|
||||
fallthrough
|
||||
case 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z':
|
||||
cAndShellQuoteCompat = true
|
||||
break
|
||||
default:
|
||||
printable := strconv.IsPrint(c)
|
||||
cAndShellQuoteCompat = printable
|
||||
if backslashEscapes && !printable {
|
||||
startESC()
|
||||
store('x')
|
||||
switch {
|
||||
case c < ' ':
|
||||
store(byte(c) >> 4)
|
||||
store(byte(c) & 0xF)
|
||||
case c > utf8.MaxRune:
|
||||
c = 0xFFFD
|
||||
fallthrough
|
||||
case c < 0x10000:
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
store(c >> uint(s) & 0xF)
|
||||
}
|
||||
default:
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
store(c >> uint(s) & 0xF)
|
||||
}
|
||||
}
|
||||
goto StoreC
|
||||
} else if isRightQuote {
|
||||
store('\\')
|
||||
isRightQuote = false
|
||||
}
|
||||
goto StoreC
|
||||
}
|
||||
}
|
||||
|
||||
if !(((backslashEscapes && style != ShellAlwaysQuotingStyle) ||
|
||||
elideOuterQuotes) && !isRightQuote) {
|
||||
goto StoreC
|
||||
}
|
||||
|
||||
StoreEscape:
|
||||
startESC()
|
||||
|
||||
StoreC:
|
||||
endESC()
|
||||
store(c)
|
||||
|
||||
if !cAndShellQuoteCompat {
|
||||
allCAndShellQuoteCompat = false
|
||||
}
|
||||
|
||||
if len == 0 && style == ShellAlwaysQuotingStyle && elideOuterQuotes {
|
||||
goto ForceOuterQuotingStyle
|
||||
}
|
||||
|
||||
ForceOuterQuotingStyle:
|
||||
if style == ShellAlwaysQuotingStyle && backslashEscapes {
|
||||
style = ShellEscapeAlwaysQuotingStyle
|
||||
}
|
||||
QuoteargBufferRestyled(
|
||||
buffer,
|
||||
arg,
|
||||
style,
|
||||
flags & ^QAElideOuterQuotes,
|
||||
0,
|
||||
leftQuote,
|
||||
rightQuote,
|
||||
)
|
||||
}
|
||||
Loading…
Reference in new issue