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.
quotearg/quotearg.go

385 lines
7.0 KiB

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,
)
}