/ errata.go
errata.go
1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "net/http" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 "time" 13 14 "golang.org/x/net/html" 15 ) 16 17 // Erratum are our individual chunks of errata 18 type Erratum struct { 19 ID int 20 Date time.Time 21 Desc string 22 Patch string 23 Link string 24 Sig []string 25 } 26 27 // Fetch pulls down and parses the information for a given Erratum 28 func (e *Erratum) Fetch() error { 29 resp, err := http.Get(e.Link) 30 if err != nil { 31 return err 32 } 33 34 defer resp.Body.Close() 35 36 scanner := bufio.NewScanner(resp.Body) 37 38 // First two lines are our signature 39 // 3rd line is date 40 // everything from the date down to ^Index is our description 41 count := 0 42 var descr []string 43 var patch []string 44 re := regexp.MustCompile(`^Index:`) 45 matched := false 46 for scanner.Scan() { 47 s := scanner.Text() 48 if count < 2 { 49 e.Sig = append(e.Sig, s) 50 } 51 52 if count == 3 { 53 parts := strings.Split(s, ",") 54 if len(parts) == 3 { 55 d := fmt.Sprintf("%s,%s", 56 strings.Trim(parts[1], " "), 57 strings.Replace(parts[2], ":", "", -1)) 58 e.Date, err = time.Parse("January 2, 2006", d) 59 if err != nil { 60 return err 61 } 62 } 63 } 64 65 if count > 3 { 66 if re.MatchString(s) { 67 matched = true 68 } 69 70 if !matched { 71 descr = append(descr, s) 72 } else { 73 patch = append(patch, s) 74 } 75 76 } 77 78 count = count + 1 79 } 80 81 if err := scanner.Err(); err != nil { 82 return err 83 } 84 85 e.Desc = strings.Join(descr, "\n") 86 e.Patch = strings.Join(patch, "\n") 87 88 return nil //fmt.Errorf("crap") 89 } 90 91 // Errata is a collection of Errata 92 type Errata struct { 93 List []Erratum 94 Length int 95 } 96 97 // By is the type of a "less" function that defines the ordering of its Planet arguments. 98 type By func(p1, p2 *Erratum) bool 99 100 // Sort is a method on the function type, By, that sorts the argument slice according to the function. 101 func (by By) Sort(errata []Erratum) { 102 es := &errataSorter{ 103 errata: errata, 104 by: by, 105 } 106 sort.Sort(es) 107 } 108 109 // errataSorter joins a By function and a slice of Errata to be sorted. 110 type errataSorter struct { 111 errata []Erratum 112 by func(p1, p2 *Erratum) bool // Closure used in the Less method. 113 } 114 115 // Len is part of sort.Interface. 116 func (s *errataSorter) Len() int { 117 return len(s.errata) 118 } 119 120 // Swap is part of sort.Interface. 121 func (s *errataSorter) Swap(i, j int) { 122 s.errata[i], s.errata[j] = s.errata[j], s.errata[i] 123 } 124 125 // Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. 126 func (s *errataSorter) Less(i, j int) bool { 127 return s.by(&s.errata[i], &s.errata[j]) 128 } 129 130 // ParseErrata does the actual parsing of html 131 func ParseErrata(body io.Reader, baseURL string) (*Errata, error) { 132 var data []Erratum 133 var errata = &Errata{} 134 doc, err := html.Parse(body) 135 if err != nil { 136 return nil, err 137 } 138 var f func(*html.Node) 139 f = func(node *html.Node) { 140 if node.Type == html.ElementNode && node.Data == "a" { 141 if node.FirstChild != nil { 142 var e Erratum 143 var err error 144 145 for _, a := range node.Attr { 146 if a.Key == "href" { 147 parts := strings.Split(a.Val, "_") 148 149 if len(parts) >= 2 && a.Val != "" { 150 e.Link = fmt.Sprintf("%s%s", baseURL, a.Val) 151 e.ID, err = strconv.Atoi(parts[0]) 152 if err != nil { 153 // Not a link we care about 154 break 155 } 156 data = append(data, e) 157 } 158 } 159 } 160 return 161 } 162 } 163 for child := node.FirstChild; child != nil; child = child.NextSibling { 164 f(child) 165 } 166 } 167 f(doc) 168 169 byID := func(p1, p2 *Erratum) bool { 170 return p1.ID < p2.ID 171 } 172 173 By(byID).Sort(data) 174 errata.List = data 175 176 return errata, nil 177 } 178 179 // ParseRemoteErrata grabs all of the OpenBSD errata from an html page 180 func ParseRemoteErrata(s string) (*Errata, error) { 181 resp, err := http.Get(s) 182 if err != nil { 183 return nil, err 184 } 185 186 defer resp.Body.Close() 187 188 return ParseErrata(resp.Body, s) 189 } 190 191 // PrintErrata pretty prints an errata 192 //func PrintErrata(e *Erratum) string { 193 // return fmt.Sprintf("New OpenBSD Errata: %03d\n%s: %s\n%s", 194 // e.ID, 195 // e.Date.String(), 196 // e.Desc, 197 // e.Link, 198 // ) 199 //} 200 201 // PrintErrataMD pretty prints an errata in markdown 202 func PrintErrataMD(e *Erratum) string { 203 return fmt.Sprintf("# OpenBSD Errata %03d ( _%s_ )\n<pre>%s</pre>\n[A source code patch exists which remedies this problem.](%s)", 204 e.ID, 205 e.Date.Format("January 2, 2006"), 206 e.Desc, 207 e.Link, 208 ) 209 }