mirror of
https://github.com/mikefarah/yq.git
synced 2026-09-05 17:36:59 +08:00
Fixing handling of CRLF #2352
This commit is contained in:
+69
-41
@@ -11,6 +11,13 @@ import (
|
||||
yaml "go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
commentLineRe = regexp.MustCompile(`^\s*#`)
|
||||
yamlDirectiveLineRe = regexp.MustCompile(`^\s*%YAML`)
|
||||
separatorLineRe = regexp.MustCompile(`^\s*---\s*$`)
|
||||
separatorPrefixRe = regexp.MustCompile(`^\s*---\s+`)
|
||||
)
|
||||
|
||||
type yamlDecoder struct {
|
||||
decoder yaml.Decoder
|
||||
|
||||
@@ -33,51 +40,72 @@ func NewYamlDecoder(prefs YamlPreferences) Decoder {
|
||||
}
|
||||
|
||||
func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, string, error) {
|
||||
var commentLineRegEx = regexp.MustCompile(`^\s*#`)
|
||||
var yamlDirectiveLineRegEx = regexp.MustCompile(`^\s*%YA`)
|
||||
var sb strings.Builder
|
||||
|
||||
for {
|
||||
peekBytes, err := reader.Peek(4)
|
||||
if errors.Is(err, io.EOF) {
|
||||
// EOF are handled else where..
|
||||
return reader, sb.String(), nil
|
||||
} else if err != nil {
|
||||
return reader, sb.String(), err
|
||||
} else if string(peekBytes[0]) == "\n" {
|
||||
_, err := reader.ReadString('\n')
|
||||
sb.WriteString("\n")
|
||||
if errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), nil
|
||||
} else if err != nil {
|
||||
return reader, sb.String(), err
|
||||
}
|
||||
} else if string(peekBytes) == "--- " {
|
||||
_, err := reader.ReadString(' ')
|
||||
sb.WriteString("$yqDocSeparator$\n")
|
||||
if errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), nil
|
||||
} else if err != nil {
|
||||
return reader, sb.String(), err
|
||||
}
|
||||
} else if string(peekBytes) == "---\n" {
|
||||
_, err := reader.ReadString('\n')
|
||||
sb.WriteString("$yqDocSeparator$\n")
|
||||
if errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), nil
|
||||
} else if err != nil {
|
||||
return reader, sb.String(), err
|
||||
}
|
||||
} else if commentLineRegEx.MatchString(string(peekBytes)) || yamlDirectiveLineRegEx.MatchString(string(peekBytes)) {
|
||||
line, err := reader.ReadString('\n')
|
||||
sb.WriteString(line)
|
||||
if errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), nil
|
||||
} else if err != nil {
|
||||
return reader, sb.String(), err
|
||||
}
|
||||
} else {
|
||||
line, err := reader.ReadString('\n')
|
||||
if errors.Is(err, io.EOF) && line == "" {
|
||||
// no more data
|
||||
return reader, sb.String(), nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), err
|
||||
}
|
||||
|
||||
// Determine newline style and strip it for inspection
|
||||
newline := ""
|
||||
if strings.HasSuffix(line, "\r\n") {
|
||||
newline = "\r\n"
|
||||
line = strings.TrimSuffix(line, "\r\n")
|
||||
} else if strings.HasSuffix(line, "\n") {
|
||||
newline = "\n"
|
||||
line = strings.TrimSuffix(line, "\n")
|
||||
}
|
||||
|
||||
trimmed := strings.TrimSpace(line)
|
||||
|
||||
// Document separator: exact line '---' or a '--- ' prefix followed by content
|
||||
if separatorLineRe.MatchString(trimmed) {
|
||||
sb.WriteString("$yqDocSeparator$")
|
||||
sb.WriteString(newline)
|
||||
if errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle lines that start with '--- ' followed by more content (e.g. '--- cat')
|
||||
if separatorPrefixRe.MatchString(line) {
|
||||
match := separatorPrefixRe.FindString(line)
|
||||
remainder := line[len(match):]
|
||||
// normalize separator newline: if original had none, default to LF
|
||||
sepNewline := newline
|
||||
if sepNewline == "" {
|
||||
sepNewline = "\n"
|
||||
}
|
||||
sb.WriteString("$yqDocSeparator$")
|
||||
sb.WriteString(sepNewline)
|
||||
// push the remainder back onto the reader and continue processing
|
||||
reader = bufio.NewReader(io.MultiReader(strings.NewReader(remainder), reader))
|
||||
if errors.Is(err, io.EOF) && remainder == "" {
|
||||
return reader, sb.String(), nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Comments, YAML directives, and blank lines are leading content
|
||||
if commentLineRe.MatchString(line) || yamlDirectiveLineRe.MatchString(line) || trimmed == "" {
|
||||
sb.WriteString(line)
|
||||
sb.WriteString(newline)
|
||||
if errors.Is(err, io.EOF) {
|
||||
return reader, sb.String(), nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// First non-leading line: push it back onto a reader and return
|
||||
originalLine := line + newline
|
||||
return io.MultiReader(strings.NewReader(originalLine), reader), sb.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user