mcchunkie/errata.go

210 lines
4.2 KiB
Go
Raw Normal View History

package main
import (
2020-03-29 15:42:12 -06:00
"bufio"
2020-02-24 18:05:00 -07:00
"fmt"
"io"
"net/http"
2020-03-29 15:42:12 -06:00
"regexp"
2020-02-24 18:05:00 -07:00
"sort"
"strconv"
"strings"
"time"
"golang.org/x/net/html"
)
2020-03-29 15:42:12 -06:00
// Erratum are our individual chunks of errata
type Erratum struct {
ID int
Date time.Time
Desc string
Patch string
Link string
Sig []string
}
// Fetch pulls down and parses the information for a given Erratum
func (e *Erratum) Fetch() error {
resp, err := http.Get(e.Link)
if err != nil {
return err
}
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
// First two lines are our signature
// 3rd line is date
// everything from the date down to ^Index is our description
count := 0
var descr []string
var patch []string
re := regexp.MustCompile(`^Index:`)
matched := false
for scanner.Scan() {
s := scanner.Text()
if count < 2 {
e.Sig = append(e.Sig, s)
}
if count == 3 {
parts := strings.Split(s, ",")
if len(parts) == 3 {
d := fmt.Sprintf("%s,%s",
strings.Trim(parts[1], " "),
strings.Replace(parts[2], ":", "", -1))
e.Date, err = time.Parse("January 2, 2006", d)
if err != nil {
return err
}
}
}
if count > 3 {
if re.MatchString(s) {
matched = true
}
if !matched {
descr = append(descr, s)
} else {
patch = append(patch, s)
}
}
count = count + 1
}
if err := scanner.Err(); err != nil {
return err
}
e.Desc = strings.Join(descr, "\n")
e.Patch = strings.Join(patch, "\n")
return nil //fmt.Errorf("crap")
2020-02-24 18:05:00 -07:00
}
2020-03-29 15:42:12 -06:00
// Errata is a collection of Errata
type Errata struct {
List []Erratum
2020-02-24 18:05:00 -07:00
Length int
}
// By is the type of a "less" function that defines the ordering of its Planet arguments.
2020-03-29 15:42:12 -06:00
type By func(p1, p2 *Erratum) bool
2020-02-24 18:05:00 -07:00
// Sort is a method on the function type, By, that sorts the argument slice according to the function.
2020-03-29 15:42:12 -06:00
func (by By) Sort(errata []Erratum) {
2020-02-24 18:05:00 -07:00
es := &errataSorter{
2020-03-29 15:42:12 -06:00
errata: errata,
by: by,
}
2020-02-24 18:05:00 -07:00
sort.Sort(es)
}
2020-03-29 15:42:12 -06:00
// errataSorter joins a By function and a slice of Errata to be sorted.
2020-02-24 18:05:00 -07:00
type errataSorter struct {
2020-03-29 15:42:12 -06:00
errata []Erratum
by func(p1, p2 *Erratum) bool // Closure used in the Less method.
2020-02-24 18:05:00 -07:00
}
// Len is part of sort.Interface.
func (s *errataSorter) Len() int {
2020-03-29 15:42:12 -06:00
return len(s.errata)
2020-02-24 18:05:00 -07:00
}
// Swap is part of sort.Interface.
func (s *errataSorter) Swap(i, j int) {
2020-03-29 15:42:12 -06:00
s.errata[i], s.errata[j] = s.errata[j], s.errata[i]
2020-02-24 18:05:00 -07:00
}
2020-02-24 18:05:00 -07:00
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s *errataSorter) Less(i, j int) bool {
2020-03-29 15:42:12 -06:00
return s.by(&s.errata[i], &s.errata[j])
2020-02-24 18:05:00 -07:00
}
2020-03-29 15:42:12 -06:00
// ParseErrata does the actual parsing of html
func ParseErrata(body io.Reader, baseURL string) (*Errata, error) {
var data []Erratum
var errata = &Errata{}
2020-02-24 18:05:00 -07:00
doc, err := html.Parse(body)
if err != nil {
return nil, err
}
var f func(*html.Node)
f = func(node *html.Node) {
2020-03-29 15:42:12 -06:00
if node.Type == html.ElementNode && node.Data == "a" {
if node.FirstChild != nil {
2020-03-29 15:42:12 -06:00
var e Erratum
2020-02-24 18:05:00 -07:00
var err error
2020-03-29 15:42:12 -06:00
for _, a := range node.Attr {
if a.Key == "href" {
parts := strings.Split(a.Val, "_")
if len(parts) >= 2 && a.Val != "" {
e.Link = fmt.Sprintf("%s%s", baseURL, a.Val)
e.ID, err = strconv.Atoi(parts[0])
if err != nil {
// Not a link we care about
break
}
data = append(data, e)
2020-02-24 18:05:00 -07:00
}
}
}
return
}
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
f(child)
}
}
f(doc)
2020-03-29 15:42:12 -06:00
byID := func(p1, p2 *Erratum) bool {
return p1.ID < p2.ID
2020-02-24 18:05:00 -07:00
}
2020-03-29 15:42:12 -06:00
By(byID).Sort(data)
errata.List = data
2020-02-24 18:05:00 -07:00
2020-03-29 15:42:12 -06:00
return errata, nil
}
2020-02-24 18:05:00 -07:00
// ParseRemoteErrata grabs all of the OpenBSD errata from an html page
2020-03-29 15:42:12 -06:00
func ParseRemoteErrata(s string) (*Errata, error) {
2020-02-24 18:05:00 -07:00
resp, err := http.Get(s)
if err != nil {
return nil, err
}
defer resp.Body.Close()
2020-03-29 15:42:12 -06:00
return ParseErrata(resp.Body, s)
2020-02-24 18:05:00 -07:00
}
// PrintErrata pretty prints an errata
//func PrintErrata(e *Erratum) string {
// return fmt.Sprintf("New OpenBSD Errata: %03d\n%s: %s\n%s",
// e.ID,
// e.Date.String(),
// e.Desc,
// e.Link,
// )
//}
2020-02-24 18:05:00 -07:00
// PrintErrataMD pretty prints an errata in markdown
2020-03-29 15:42:12 -06:00
func PrintErrataMD(e *Erratum) string {
return fmt.Sprintf("# OpenBSD Errata %03d ( _%s_ )\n<pre>%s</pre>\n[A source code patch exists which remedies this problem.](%s)",
2020-03-29 15:42:12 -06:00
e.ID,
e.Date.Format("January 2, 2006"),
e.Desc,
e.Link,
)
}