blob: e398dbff8e9f9ab7b6975cb8868592720251bb35 [file] [log] [blame]
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package email
import (
"bufio"
"fmt"
"regexp"
"strings"
)
func ParsePatch(text string) (title string, diff string, err error) {
s := bufio.NewScanner(strings.NewReader(text))
lastLine := ""
diffStarted := false
for s.Scan() {
ln := s.Text()
if lineMatchesDiffStart(ln) {
diffStarted = true
diff += ln + "\n"
if title == "" {
title = lastLine
}
continue
}
if diffStarted {
if ln == "" || ln == "--" || ln == "-- " || ln[0] == '>' {
diffStarted = false
continue
}
if strings.HasPrefix(ln, " ") || strings.HasPrefix(ln, "+") ||
strings.HasPrefix(ln, "-") || strings.HasPrefix(ln, "@") ||
strings.HasPrefix(ln, "================") {
diff += ln + "\n"
continue
}
}
if strings.HasPrefix(ln, "Subject: ") {
title = ln[len("Subject: "):]
continue
}
if ln == "" || title != "" || diffStarted {
continue
}
lastLine = ln
if strings.HasPrefix(ln, " ") {
title = ln[4:]
}
}
if err = s.Err(); err != nil {
return
}
if strings.Contains(strings.ToLower(title), "[patch") {
pos := strings.IndexByte(title, ']')
if pos == -1 {
err = fmt.Errorf("title contains '[patch' but not ']'")
return
}
title = title[pos+1:]
}
title = strings.TrimSpace(title)
if title == "" {
err = fmt.Errorf("failed to extract title")
return
}
if diff == "" {
err = fmt.Errorf("failed to extract diff")
return
}
return
}
func lineMatchesDiffStart(ln string) bool {
diffRegexps := []*regexp.Regexp{
regexp.MustCompile(`^(---|\+\+\+) [^\s]`),
regexp.MustCompile(`^diff --git`),
regexp.MustCompile(`^index [0-9a-f]+\.\.[0-9a-f]+`),
regexp.MustCompile(`^new file mode [0-9]+`),
regexp.MustCompile(`^Index: [^\s]`),
}
for _, re := range diffRegexps {
if re.MatchString(ln) {
return true
}
}
return false
}