// +build !cvis 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 } *s = stateGround return unvisErrSynbad } 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 */ )