package addressparsing import "strings" // FixUnquotedMarkers quotes unquoted "<>,;:@". // // Deprecated: Doesn't seem to be useful: `x @y .com` -> `x "@y" ".com"` != `"x@y.com"` func FixUnquotedMarkers(s string) string { const markers = "<>,;:@" b := new(strings.Builder) FmapSplitByWhitespaceQuotedText(s, func(z string) { switch { case IsWS(z[0]) || z[0] == '"': b.WriteString(z) case strings.IndexAny(z, markers) == -1: b.WriteString(z) default: b.WriteString(QuoteString(z)) } }) return b.String() } // BUG(jfrech): 2024-06-12: This function should be called "Quote". func QuoteString(s string) string { var n int for _, c := range []byte(s) { if c == '"' || c == '\\' { n++ } } n += len(s) if n < 0 { panic("len overflow") } n += 2 if n < 0 { panic("len overflow") } buf := make([]byte, n) buf[0] = '"' buf[len(buf)-1] = '"' b := buf[1:len(buf)-1] // fast-path: nothing interior to quote if n == len(s)+2 { copy(b, s) return string(buf) } for _, c := range []byte(s) { if c == '"' || c == '\\' { b[0] = '\\' b = b[1:] } b[0] = c b = b[1:] } if len(b) != 0 { panic("unreachable") } return string(buf) } // FmapSplitByWhitespaceQuotedText calls f with successive non-zero slices // out of s which are split by whitespace/quote/text edges. func FmapSplitByWhitespaceQuotedText(s string, f func(z string)) { for len(s) > 0 { k := func() (k int) { switch { case s[0] == '"': loop: for k = 1; k < len(s); k++ { switch s[k] { case '"': break loop case '\\': k++ } } return min(len(s), k+1) case IsWS(s[0]): for k < len(s) && IsWS(s[k]) { k++ } return k default: for k < len(s) && !IsWS(s[k]) && s[k] != '"' { k++ } return k } }() if k <= 0 { panic("unreachable") } f(s[:k]) s = s[k:] } } func QuoteIfNecessary(s string) string { if strings.IndexAny(s, "\"\\" + "<>:;," + "@" + "\r\n\t " + "()") == -1 { return s } return QuoteString(s) }