/*Translate file names for usage on restrictive storage systems The restricted set of characters are mapped to a unicode equivalent version(most to their FULLWIDTH variant) to increase compatability with otherstorage systems.See: http://unicode-search.net/unicode-namesearch.pl?term=FULLWIDTH Encoders will also quote reserved characters to differentiate betweenthe raw and encoded forms.*/ package encoder import ( "bytes" "fmt" "io" "strconv" "strings" "unicode/utf8") const ( // adding this to any printable ASCII character turns it into the // FULLWIDTH variant fullOffset = 0xFEE0 // the first rune of the SYMBOL FOR block for control characters symbolOffset = '␀' // SYMBOL FOR NULL // QuoteRune is the rune used for quoting reserved characters QuoteRune = '‛' // SINGLE HIGH-REVERSED-9 QUOTATION MARK // EncodeStandard contains the flags used for the Standard Encoder EncodeStandard = EncodeZero | EncodeSlash | EncodeCtl | EncodeDel // Standard defines the encoding that is used for paths in- and output by rclone. // // List of replaced characters: // (0x00) -> '␀' // SYMBOL FOR NULL // / (slash) -> '/' // FULLWIDTH SOLIDUS Standard = MultiEncoder(EncodeStandard)) // Possible flags for the MultiEncoderconst ( EncodeZero uint = 0 // NUL(0x00) EncodeSlash uint = 1 << iota // / EncodeWin // :?"*<>| EncodeBackSlash // \ EncodeHashPercent // #% EncodeDel // DEL(0x7F) EncodeCtl // CTRL(0x01-0x1F) EncodeLeftSpace // Leading SPACE EncodeLeftTilde // Leading ~ EncodeRightSpace // Trailing SPACE EncodeRightPeriod // Trailing . EncodeInvalidUtf8 // Invalid UTF-8 bytes) // Encoder can transform names to and from the original and translated version.type Encoder interface { // Encode takes a raw name and substitutes any reserved characters and // patterns in it Encode(string) string // Decode takes a name and undoes any substitutions made by Encode Decode(string) string // FromStandardPath takes a / separated path in Standard encoding // and converts it to a / separated path in this encoding. FromStandardPath(string) string // FromStandardName takes name in Standard encoding and converts // it in this encoding. FromStandardName(string) string // ToStandardPath takes a / separated path in this encoding // and converts it to a / separated path in Standard encoding. ToStandardPath(string) string // ToStandardName takes name in this encoding and converts // it in Standard encoding. ToStandardName(string) string} // MultiEncoder is a configurable Encoder. The Encode* constants in this// package can be combined using bitwise or (|) to enable handling of multiple// character classestype MultiEncoder uint // Encode takes a raw name and substitutes any reserved characters and// patterns in itfunc (mask MultiEncoder) Encode(in string) string { var ( encodeWin = uint(mask)&EncodeWin != 0 encodeSlash = uint(mask)&EncodeSlash != 0 encodeBackSlash = uint(mask)&EncodeBackSlash != 0 encodeHashPercent = uint(mask)&EncodeHashPercent != 0 encodeDel = uint(mask)&EncodeDel != 0 encodeCtl = uint(mask)&EncodeCtl != 0 encodeLeftSpace = uint(mask)&EncodeLeftSpace != 0 encodeLeftTilde = uint(mask)&EncodeLeftTilde != 0 encodeRightSpace = uint(mask)&EncodeRightSpace != 0 encodeRightPeriod = uint(mask)&EncodeRightPeriod != 0 encodeInvalidUnicode = uint(mask)&EncodeInvalidUtf8 != 0 ) // handle prefix only replacements prefix := "" if encodeLeftSpace && len(in) > 0 { // Leading SPACE if in[0] == ' ' { prefix, in = "␠", in[1:] // SYMBOL FOR SPACE } else if r, l := utf8.DecodeRuneInString(in); r == '␠' { // SYMBOL FOR SPACE prefix, in = string(QuoteRune)+"␠", in[l:] // SYMBOL FOR SPACE } } if encodeLeftTilde && len(in) > 0 { // Leading ~ if in[0] == '~' { prefix, in = string('~'+fullOffset), in[1:] // FULLWIDTH TILDE } else if r, l := utf8.DecodeRuneInString(in); r == '~'+fullOffset { prefix, in = string(QuoteRune)+string('~'+fullOffset), in[l:] // FULLWIDTH TILDE } } // handle suffix only replacements suffix := "" if encodeRightSpace && len(in) > 0 { // Trailing SPACE if in[len(in)-1] == ' ' { suffix, in = "␠", in[:len(in)-1] // SYMBOL FOR SPACE } else if r, l := utf8.DecodeLastRuneInString(in); r == '␠' { suffix, in = string(QuoteRune)+"␠", in[:len(in)-l] // SYMBOL FOR SPACE } } if encodeRightPeriod && len(in) > 0 { // Trailing . if in[len(in)-1] == '.' { suffix, in = ".", in[:len(in)-1] // FULLWIDTH FULL STOP } else if r, l := utf8.DecodeLastRuneInString(in); r == '.' { suffix, in = string(QuoteRune)+".", in[:len(in)-l] // FULLWIDTH FULL STOP } } index := 0 if prefix == "" && suffix == "" { // find the first rune which (most likely) needs to be replaced index = strings.IndexFunc(in, func(r rune) bool { switch r { case 0, '␀', QuoteRune, utf8.RuneError: return true } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"', '*', '<', '>', '?', ':', '|', '"': return true } } if encodeSlash { // / switch r { case '/', '/': return true } } if encodeBackSlash { // \ switch r { case '\\', '\': return true } } if encodeHashPercent { // #% switch r { case '#', '%', '#', '%': return true } } if encodeDel { // DEL(0x7F) switch r { case rune(0x7F), '␡': return true } } if encodeCtl { // CTRL(0x01-0x1F) if r >= 1 && r <= 0x1F { return true } else if r > symbolOffset && r <= symbolOffset+0x1F { return true } } return false }) } // nothing to replace, return input if index == -1 { return in } var out bytes.Buffer out.Grow(len(in) + len(prefix) + len(suffix)) out.WriteString(prefix) // copy the clean part of the input and skip it out.WriteString(in[:index]) in = in[index:] for i, r := range in { switch r { case 0: out.WriteRune(symbolOffset) continue case '␀', QuoteRune: out.WriteRune(QuoteRune) out.WriteRune(r) continue case utf8.RuneError: if encodeInvalidUnicode { // only encode invalid sequences and not utf8.RuneError if i+3 > len(in) || in[i:i+3] != string(utf8.RuneError) { _, l := utf8.DecodeRuneInString(in[i:]) appendQuotedBytes(&out, in[i:i+l]) continue } } else { // append the real bytes instead of utf8.RuneError _, l := utf8.DecodeRuneInString(in[i:]) out.WriteString(in[i : i+l]) continue } } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"': out.WriteRune(r + fullOffset) continue case '*', '<', '>', '?', ':', '|', '"': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeSlash { // / switch r { case '/': out.WriteRune(r + fullOffset) continue case '/': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeBackSlash { // \ switch r { case '\\': out.WriteRune(r + fullOffset) continue case '\': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeHashPercent { // #% switch r { case '#', '%': out.WriteRune(r + fullOffset) continue case '#', '%': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeDel { // DEL(0x7F) switch r { case rune(0x7F): out.WriteRune('␡') // SYMBOL FOR DELETE continue case '␡': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeCtl { // CTRL(0x01-0x1F) if r >= 1 && r <= 0x1F { out.WriteRune('␀' + r) // SYMBOL FOR NULL continue } else if r > symbolOffset && r <= symbolOffset+0x1F { out.WriteRune(QuoteRune) out.WriteRune(r) continue } } out.WriteRune(r) } out.WriteString(suffix) return out.String()} // Decode takes a name and undoes any substitutions made by Encodefunc (mask MultiEncoder) Decode(in string) string { var ( encodeWin = uint(mask)&EncodeWin != 0 encodeSlash = uint(mask)&EncodeSlash != 0 encodeBackSlash = uint(mask)&EncodeBackSlash != 0 encodeHashPercent = uint(mask)&EncodeHashPercent != 0 encodeDel = uint(mask)&EncodeDel != 0 encodeCtl = uint(mask)&EncodeCtl != 0 encodeLeftSpace = uint(mask)&EncodeLeftSpace != 0 encodeLeftTilde = uint(mask)&EncodeLeftTilde != 0 encodeRightSpace = uint(mask)&EncodeRightSpace != 0 encodeRightPeriod = uint(mask)&EncodeRightPeriod != 0 encodeInvalidUnicode = uint(mask)&EncodeInvalidUtf8 != 0 ) // handle prefix only replacements prefix := "" if r, l1 := utf8.DecodeRuneInString(in); encodeLeftSpace && r == '␠' { // SYMBOL FOR SPACE prefix, in = " ", in[l1:] } else if encodeLeftTilde && r == '~' { // FULLWIDTH TILDE prefix, in = "~", in[l1:] } else if r == QuoteRune { if r, l2 := utf8.DecodeRuneInString(in[l1:]); encodeLeftSpace && r == '␠' { // SYMBOL FOR SPACE prefix, in = "␠", in[l1+l2:] } else if encodeLeftTilde && r == '~' { // FULLWIDTH TILDE prefix, in = "~", in[l1+l2:] } } // handle suffix only replacements suffix := "" if r, l := utf8.DecodeLastRuneInString(in); encodeRightSpace && r == '␠' { // SYMBOL FOR SPACE in = in[:len(in)-l] if r, l2 := utf8.DecodeLastRuneInString(in); r == QuoteRune { suffix, in = "␠", in[:len(in)-l2] } else { suffix = " " } } else if encodeRightPeriod && r == '.' { // FULLWIDTH FULL STOP in = in[:len(in)-l] if r, l2 := utf8.DecodeLastRuneInString(in); r == QuoteRune { suffix, in = ".", in[:len(in)-l2] } else { suffix = "." } } index := 0 if prefix == "" && suffix == "" { // find the first rune which (most likely) needs to be replaced index = strings.IndexFunc(in, func(r rune) bool { switch r { case '␀', QuoteRune: return true } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"': return true } } if encodeSlash { // / switch r { case '/': return true } } if encodeBackSlash { // \ switch r { case '\': return true } } if encodeHashPercent { // #% switch r { case '#', '%': return true } } if encodeDel { // DEL(0x7F) switch r { case '␡': return true } } if encodeCtl { // CTRL(0x01-0x1F) if r > symbolOffset && r <= symbolOffset+0x1F { return true } } return false }) } // nothing to replace, return input if index == -1 { return in } var out bytes.Buffer out.Grow(len(in)) out.WriteString(prefix) // copy the clean part of the input and skip it out.WriteString(in[:index]) in = in[index:] var unquote, unquoteNext, skipNext bool for i, r := range in { if skipNext { skipNext = false continue } unquote, unquoteNext = unquoteNext, false switch r { case '␀': // SYMBOL FOR NULL if unquote { out.WriteRune(r) } else { out.WriteRune(0) } continue case QuoteRune: if unquote { out.WriteRune(r) } else { unquoteNext = true } continue } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"': if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeSlash { // / switch r { case '/': // FULLWIDTH SOLIDUS if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeBackSlash { // \ switch r { case '\': // FULLWIDTH REVERSE SOLIDUS if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeHashPercent { // #% switch r { case '#', '%': if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeDel { // DEL(0x7F) switch r { case '␡': // SYMBOL FOR DELETE if unquote { out.WriteRune(r) } else { out.WriteRune(0x7F) } continue } } if encodeCtl { // CTRL(0x01-0x1F) if r > symbolOffset && r <= symbolOffset+0x1F { if unquote { out.WriteRune(r) } else { out.WriteRune(r - symbolOffset) } continue } } if unquote { if encodeInvalidUnicode { skipNext = appendUnquotedByte(&out, in[i:]) if skipNext { continue } } out.WriteRune(QuoteRune) } switch r { case utf8.RuneError: // append the real bytes instead of utf8.RuneError _, l := utf8.DecodeRuneInString(in[i:]) out.WriteString(in[i : i+l])Lines 1–500 of 633. Load more
/* Translate file names for usage on restrictive storage systems The restricted set of characters are mapped to a unicode equivalent version (most to their FULLWIDTH variant) to increase compatability with other storage systems. See: http://unicode-search.net/unicode-namesearch.pl?term=FULLWIDTH Encoders will also quote reserved characters to differentiate between the raw and encoded forms. */ package encoder import ( "bytes" "fmt" "io" "strconv" "strings" "unicode/utf8" ) const ( // adding this to any printable ASCII character turns it into the // FULLWIDTH variant fullOffset = 0xFEE0 // the first rune of the SYMBOL FOR block for control characters symbolOffset = '␀' // SYMBOL FOR NULL // QuoteRune is the rune used for quoting reserved characters QuoteRune = '‛' // SINGLE HIGH-REVERSED-9 QUOTATION MARK // EncodeStandard contains the flags used for the Standard Encoder EncodeStandard = EncodeZero | EncodeSlash | EncodeCtl | EncodeDel // Standard defines the encoding that is used for paths in- and output by rclone. // // List of replaced characters: // (0x00) -> '␀' // SYMBOL FOR NULL // / (slash) -> '/' // FULLWIDTH SOLIDUS Standard = MultiEncoder(EncodeStandard) ) // Possible flags for the MultiEncoder const ( EncodeZero uint = 0 // NUL(0x00) EncodeSlash uint = 1 << iota // / EncodeWin // :?"*<>| EncodeBackSlash // \ EncodeHashPercent // #% EncodeDel // DEL(0x7F) EncodeCtl // CTRL(0x01-0x1F) EncodeLeftSpace // Leading SPACE EncodeLeftTilde // Leading ~ EncodeRightSpace // Trailing SPACE EncodeRightPeriod // Trailing . EncodeInvalidUtf8 // Invalid UTF-8 bytes ) // Encoder can transform names to and from the original and translated version. type Encoder interface { // Encode takes a raw name and substitutes any reserved characters and // patterns in it Encode(string) string // Decode takes a name and undoes any substitutions made by Encode Decode(string) string // FromStandardPath takes a / separated path in Standard encoding // and converts it to a / separated path in this encoding. FromStandardPath(string) string // FromStandardName takes name in Standard encoding and converts // it in this encoding. FromStandardName(string) string // ToStandardPath takes a / separated path in this encoding // and converts it to a / separated path in Standard encoding. ToStandardPath(string) string // ToStandardName takes name in this encoding and converts // it in Standard encoding. ToStandardName(string) string } // MultiEncoder is a configurable Encoder. The Encode* constants in this // package can be combined using bitwise or (|) to enable handling of multiple // character classes type MultiEncoder uint // Encode takes a raw name and substitutes any reserved characters and // patterns in it func (mask MultiEncoder) Encode(in string) string { var ( encodeWin = uint(mask)&EncodeWin != 0 encodeSlash = uint(mask)&EncodeSlash != 0 encodeBackSlash = uint(mask)&EncodeBackSlash != 0 encodeHashPercent = uint(mask)&EncodeHashPercent != 0 encodeDel = uint(mask)&EncodeDel != 0 encodeCtl = uint(mask)&EncodeCtl != 0 encodeLeftSpace = uint(mask)&EncodeLeftSpace != 0 encodeLeftTilde = uint(mask)&EncodeLeftTilde != 0 encodeRightSpace = uint(mask)&EncodeRightSpace != 0 encodeRightPeriod = uint(mask)&EncodeRightPeriod != 0 encodeInvalidUnicode = uint(mask)&EncodeInvalidUtf8 != 0 ) // handle prefix only replacements prefix := "" if encodeLeftSpace && len(in) > 0 { // Leading SPACE if in[0] == ' ' { prefix, in = "␠", in[1:] // SYMBOL FOR SPACE } else if r, l := utf8.DecodeRuneInString(in); r == '␠' { // SYMBOL FOR SPACE prefix, in = string(QuoteRune)+"␠", in[l:] // SYMBOL FOR SPACE } } if encodeLeftTilde && len(in) > 0 { // Leading ~ if in[0] == '~' { prefix, in = string('~'+fullOffset), in[1:] // FULLWIDTH TILDE } else if r, l := utf8.DecodeRuneInString(in); r == '~'+fullOffset { prefix, in = string(QuoteRune)+string('~'+fullOffset), in[l:] // FULLWIDTH TILDE } } // handle suffix only replacements suffix := "" if encodeRightSpace && len(in) > 0 { // Trailing SPACE if in[len(in)-1] == ' ' { suffix, in = "␠", in[:len(in)-1] // SYMBOL FOR SPACE } else if r, l := utf8.DecodeLastRuneInString(in); r == '␠' { suffix, in = string(QuoteRune)+"␠", in[:len(in)-l] // SYMBOL FOR SPACE } } if encodeRightPeriod && len(in) > 0 { // Trailing . if in[len(in)-1] == '.' { suffix, in = ".", in[:len(in)-1] // FULLWIDTH FULL STOP } else if r, l := utf8.DecodeLastRuneInString(in); r == '.' { suffix, in = string(QuoteRune)+".", in[:len(in)-l] // FULLWIDTH FULL STOP } } index := 0 if prefix == "" && suffix == "" { // find the first rune which (most likely) needs to be replaced index = strings.IndexFunc(in, func(r rune) bool { switch r { case 0, '␀', QuoteRune, utf8.RuneError: return true } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"', '*', '<', '>', '?', ':', '|', '"': return true } } if encodeSlash { // / switch r { case '/', '/': return true } } if encodeBackSlash { // \ switch r { case '\\', '\': return true } } if encodeHashPercent { // #% switch r { case '#', '%', '#', '%': return true } } if encodeDel { // DEL(0x7F) switch r { case rune(0x7F), '␡': return true } } if encodeCtl { // CTRL(0x01-0x1F) if r >= 1 && r <= 0x1F { return true } else if r > symbolOffset && r <= symbolOffset+0x1F { return true } } return false }) } // nothing to replace, return input if index == -1 { return in } var out bytes.Buffer out.Grow(len(in) + len(prefix) + len(suffix)) out.WriteString(prefix) // copy the clean part of the input and skip it out.WriteString(in[:index]) in = in[index:] for i, r := range in { switch r { case 0: out.WriteRune(symbolOffset) continue case '␀', QuoteRune: out.WriteRune(QuoteRune) out.WriteRune(r) continue case utf8.RuneError: if encodeInvalidUnicode { // only encode invalid sequences and not utf8.RuneError if i+3 > len(in) || in[i:i+3] != string(utf8.RuneError) { _, l := utf8.DecodeRuneInString(in[i:]) appendQuotedBytes(&out, in[i:i+l]) continue } } else { // append the real bytes instead of utf8.RuneError _, l := utf8.DecodeRuneInString(in[i:]) out.WriteString(in[i : i+l]) continue } } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"': out.WriteRune(r + fullOffset) continue case '*', '<', '>', '?', ':', '|', '"': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeSlash { // / switch r { case '/': out.WriteRune(r + fullOffset) continue case '/': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeBackSlash { // \ switch r { case '\\': out.WriteRune(r + fullOffset) continue case '\': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeHashPercent { // #% switch r { case '#', '%': out.WriteRune(r + fullOffset) continue case '#', '%': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeDel { // DEL(0x7F) switch r { case rune(0x7F): out.WriteRune('␡') // SYMBOL FOR DELETE continue case '␡': out.WriteRune(QuoteRune) out.WriteRune(r) continue } } if encodeCtl { // CTRL(0x01-0x1F) if r >= 1 && r <= 0x1F { out.WriteRune('␀' + r) // SYMBOL FOR NULL continue } else if r > symbolOffset && r <= symbolOffset+0x1F { out.WriteRune(QuoteRune) out.WriteRune(r) continue } } out.WriteRune(r) } out.WriteString(suffix) return out.String() } // Decode takes a name and undoes any substitutions made by Encode func (mask MultiEncoder) Decode(in string) string { var ( encodeWin = uint(mask)&EncodeWin != 0 encodeSlash = uint(mask)&EncodeSlash != 0 encodeBackSlash = uint(mask)&EncodeBackSlash != 0 encodeHashPercent = uint(mask)&EncodeHashPercent != 0 encodeDel = uint(mask)&EncodeDel != 0 encodeCtl = uint(mask)&EncodeCtl != 0 encodeLeftSpace = uint(mask)&EncodeLeftSpace != 0 encodeLeftTilde = uint(mask)&EncodeLeftTilde != 0 encodeRightSpace = uint(mask)&EncodeRightSpace != 0 encodeRightPeriod = uint(mask)&EncodeRightPeriod != 0 encodeInvalidUnicode = uint(mask)&EncodeInvalidUtf8 != 0 ) // handle prefix only replacements prefix := "" if r, l1 := utf8.DecodeRuneInString(in); encodeLeftSpace && r == '␠' { // SYMBOL FOR SPACE prefix, in = " ", in[l1:] } else if encodeLeftTilde && r == '~' { // FULLWIDTH TILDE prefix, in = "~", in[l1:] } else if r == QuoteRune { if r, l2 := utf8.DecodeRuneInString(in[l1:]); encodeLeftSpace && r == '␠' { // SYMBOL FOR SPACE prefix, in = "␠", in[l1+l2:] } else if encodeLeftTilde && r == '~' { // FULLWIDTH TILDE prefix, in = "~", in[l1+l2:] } } // handle suffix only replacements suffix := "" if r, l := utf8.DecodeLastRuneInString(in); encodeRightSpace && r == '␠' { // SYMBOL FOR SPACE in = in[:len(in)-l] if r, l2 := utf8.DecodeLastRuneInString(in); r == QuoteRune { suffix, in = "␠", in[:len(in)-l2] } else { suffix = " " } } else if encodeRightPeriod && r == '.' { // FULLWIDTH FULL STOP in = in[:len(in)-l] if r, l2 := utf8.DecodeLastRuneInString(in); r == QuoteRune { suffix, in = ".", in[:len(in)-l2] } else { suffix = "." } } index := 0 if prefix == "" && suffix == "" { // find the first rune which (most likely) needs to be replaced index = strings.IndexFunc(in, func(r rune) bool { switch r { case '␀', QuoteRune: return true } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"': return true } } if encodeSlash { // / switch r { case '/': return true } } if encodeBackSlash { // \ switch r { case '\': return true } } if encodeHashPercent { // #% switch r { case '#', '%': return true } } if encodeDel { // DEL(0x7F) switch r { case '␡': return true } } if encodeCtl { // CTRL(0x01-0x1F) if r > symbolOffset && r <= symbolOffset+0x1F { return true } } return false }) } // nothing to replace, return input if index == -1 { return in } var out bytes.Buffer out.Grow(len(in)) out.WriteString(prefix) // copy the clean part of the input and skip it out.WriteString(in[:index]) in = in[index:] var unquote, unquoteNext, skipNext bool for i, r := range in { if skipNext { skipNext = false continue } unquote, unquoteNext = unquoteNext, false switch r { case '␀': // SYMBOL FOR NULL if unquote { out.WriteRune(r) } else { out.WriteRune(0) } continue case QuoteRune: if unquote { out.WriteRune(r) } else { unquoteNext = true } continue } if encodeWin { // :?"*<>| switch r { case '*', '<', '>', '?', ':', '|', '"': if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeSlash { // / switch r { case '/': // FULLWIDTH SOLIDUS if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeBackSlash { // \ switch r { case '\': // FULLWIDTH REVERSE SOLIDUS if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeHashPercent { // #% switch r { case '#', '%': if unquote { out.WriteRune(r) } else { out.WriteRune(r - fullOffset) } continue } } if encodeDel { // DEL(0x7F) switch r { case '␡': // SYMBOL FOR DELETE if unquote { out.WriteRune(r) } else { out.WriteRune(0x7F) } continue } } if encodeCtl { // CTRL(0x01-0x1F) if r > symbolOffset && r <= symbolOffset+0x1F { if unquote { out.WriteRune(r) } else { out.WriteRune(r - symbolOffset) } continue } } if unquote { if encodeInvalidUnicode { skipNext = appendUnquotedByte(&out, in[i:]) if skipNext { continue } } out.WriteRune(QuoteRune) } switch r { case utf8.RuneError: // append the real bytes instead of utf8.RuneError _, l := utf8.DecodeRuneInString(in[i:]) out.WriteString(in[i : i+l])