231 lines
4.5 KiB
Go
231 lines
4.5 KiB
Go
|
// +build !cgo govis
|
||
|
|
||
|
package mtree
|
||
|
|
||
|
import "unicode"
|
||
|
|
||
|
func unvis(src string) (string, error) {
|
||
|
dst := &[]byte{}
|
||
|
var s state
|
||
|
for i, r := range src {
|
||
|
again:
|
||
|
err := unvisRune(dst, r, &s, 0)
|
||
|
switch err {
|
||
|
case unvisValid:
|
||
|
break
|
||
|
case unvisValidPush:
|
||
|
goto again
|
||
|
case unvisNone:
|
||
|
fallthrough
|
||
|
case unvisNochar:
|
||
|
break
|
||
|
default:
|
||
|
return "", err
|
||
|
}
|
||
|
if i == len(src)-1 {
|
||
|
unvisRune(dst, r, &s, unvisEnd)
|
||
|
}
|
||
|
}
|
||
|
return string(*dst), nil
|
||
|
}
|
||
|
|
||
|
func unvisRune(dst *[]byte, r rune, s *state, flags VisFlag) error {
|
||
|
if (flags & unvisEnd) != 0 {
|
||
|
if *s == stateOctal2 || *s == stateOctal3 {
|
||
|
*s = stateGround
|
||
|
return unvisValid
|
||
|
}
|
||
|
if *s == stateGround {
|
||
|
return unvisNochar
|
||
|
}
|
||
|
return unvisErrSynbad
|
||
|
}
|
||
|
|
||
|
switch *s & ^stateHTTP {
|
||
|
case stateGround:
|
||
|
if r == '\\' {
|
||
|
*s = stateStart
|
||
|
return unvisNone
|
||
|
}
|
||
|
if flags&VisHttpstyle != 0 && r == '%' {
|
||
|
*s = stateStart | stateHTTP
|
||
|
return unvisNone
|
||
|
}
|
||
|
*dst = append(*dst, byte(r))
|
||
|
return unvisValid
|
||
|
case stateStart:
|
||
|
if *s&stateHTTP != 0 && ishex(unicode.ToLower(r)) {
|
||
|
if unicode.IsNumber(r) {
|
||
|
*dst = append(*dst, byte(r-'0'))
|
||
|
} else {
|
||
|
*dst = append(*dst, byte(unicode.ToLower(r)-'a'))
|
||
|
}
|
||
|
*s = stateHex2
|
||
|
return unvisNone
|
||
|
}
|
||
|
switch r {
|
||
|
case '\\':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, byte(r))
|
||
|
return unvisValid
|
||
|
case '0':
|
||
|
fallthrough
|
||
|
case '1':
|
||
|
fallthrough
|
||
|
case '2':
|
||
|
fallthrough
|
||
|
case '3':
|
||
|
fallthrough
|
||
|
case '4':
|
||
|
fallthrough
|
||
|
case '5':
|
||
|
fallthrough
|
||
|
case '6':
|
||
|
fallthrough
|
||
|
case '7':
|
||
|
*s = stateOctal2
|
||
|
*dst = append(*dst, byte(r-'0'))
|
||
|
return unvisNone
|
||
|
case 'M':
|
||
|
*s = stateMeta
|
||
|
*dst = append(*dst, 0200)
|
||
|
return unvisNone
|
||
|
case '^':
|
||
|
*s = stateCtrl
|
||
|
return unvisNone
|
||
|
case 'n':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\n')
|
||
|
return unvisValid
|
||
|
case 'r':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\r')
|
||
|
return unvisValid
|
||
|
case 'b':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\b')
|
||
|
return unvisValid
|
||
|
case 'a':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\007')
|
||
|
return unvisValid
|
||
|
case 'v':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\v')
|
||
|
return unvisValid
|
||
|
case 't':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\t')
|
||
|
return unvisValid
|
||
|
case 'f':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\f')
|
||
|
return unvisValid
|
||
|
case 's':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, ' ')
|
||
|
return unvisValid
|
||
|
case 'E':
|
||
|
*s = stateGround
|
||
|
*dst = append(*dst, '\033')
|
||
|
return unvisValid
|
||
|
case '\n':
|
||
|
// hidden newline
|
||
|
*s = stateGround
|
||
|
return unvisNochar
|
||
|
case '$':
|
||
|
// hidden marker
|
||
|
*s = stateGround
|
||
|
return unvisNochar
|
||
|
}
|
||
|
*s = stateGround
|
||
|
return unvisErrSynbad
|
||
|
case stateMeta:
|
||
|
if r == '-' {
|
||
|
*s = stateMeta1
|
||
|
} else if r == '^' {
|
||
|
*s = stateCtrl
|
||
|
} else {
|
||
|
*s = stateGround
|
||
|
return unvisErrSynbad
|
||
|
}
|
||
|
return unvisNone
|
||
|
case stateMeta1:
|
||
|
*s = stateGround
|
||
|
dp := *dst
|
||
|
dp[len(dp)-1] |= byte(r)
|
||
|
return unvisValid
|
||
|
case stateCtrl:
|
||
|
dp := *dst
|
||
|
if r == '?' {
|
||
|
dp[len(dp)-1] |= 0177
|
||
|
} else {
|
||
|
dp[len(dp)-1] |= byte(r & 037)
|
||
|
}
|
||
|
*s = stateGround
|
||
|
return unvisValid
|
||
|
case stateOctal2:
|
||
|
if isoctal(r) {
|
||
|
dp := *dst
|
||
|
if len(dp) > 0 {
|
||
|
last := dp[len(dp)-1]
|
||
|
dp[len(dp)-1] = (last << 3) + byte(r-'0')
|
||
|
} else {
|
||
|
dp = append(dp, byte((0<<3)+(r-'0')))
|
||
|
}
|
||
|
*s = stateOctal3
|
||
|
return unvisNone
|
||
|
}
|
||
|
*s = stateGround
|
||
|
return unvisValidPush
|
||
|
case stateOctal3:
|
||
|
*s = stateGround
|
||
|
if isoctal(r) {
|
||
|
dp := *dst
|
||
|
if len(dp) > 0 {
|
||
|
last := dp[len(dp)-1]
|
||
|
dp[len(dp)-1] = (last << 3) + byte(r-'0')
|
||
|
} else {
|
||
|
dp = append(dp, (0<<3)+byte(r-'0'))
|
||
|
}
|
||
|
return unvisValid
|
||
|
}
|
||
|
return unvisValidPush
|
||
|
case stateHex2:
|
||
|
if ishex(unicode.ToLower(r)) {
|
||
|
last := byte(0)
|
||
|
dp := *dst
|
||
|
if len(dp) > 0 {
|
||
|
last = dp[len(dp)-1]
|
||
|
}
|
||
|
if unicode.IsNumber(r) {
|
||
|
dp = append(dp, (last<<4)+byte(r-'0'))
|
||
|
} else {
|
||
|
dp = append(dp, (last<<4)+byte(unicode.ToLower(r)-'a'+10))
|
||
|
}
|
||
|
}
|
||
|
*s = stateGround
|
||
|
return unvisValid
|
||
|
default:
|
||
|
*s = stateGround
|
||
|
return unvisErrSynbad
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type state int
|
||
|
|
||
|
const (
|
||
|
stateGround state = iota /* haven't seen escape char */
|
||
|
stateStart /* start decoding special sequence */
|
||
|
stateMeta /* metachar started (M) */
|
||
|
stateMeta1 /* metachar more, regular char (-) */
|
||
|
stateCtrl /* control char started (^) */
|
||
|
stateOctal2 /* octal digit 2 */
|
||
|
stateOctal3 /* octal digit 3 */
|
||
|
stateHex2 /* hex digit 2 */
|
||
|
|
||
|
stateHTTP state = 0x080 /* %HEXHEX escape */
|
||
|
)
|