-
-
Notifications
You must be signed in to change notification settings - Fork 12
/
statuscrawler.go
171 lines (148 loc) · 4.63 KB
/
statuscrawler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//+build ignore
// simple crawler to extract Status Codes from MSDN.
package main
import (
"bytes"
"fmt"
"go/format"
"log"
"os"
"regexp"
"sort"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"net/http"
)
const URL = "https://msdn.microsoft.com/en-us/library/cc704588.aspx"
type status struct {
val uint64
name string
desc string
}
type StatusList []status
func (l StatusList) Len() int { return len(l) }
func (l StatusList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l StatusList) Less(i, j int) bool { return l[i].val < l[j].val }
var list StatusList
func main() {
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
log.Fatalf("Could not construct request: %v", err)
}
req.Header.Add("User-Agent", "Mozilla (compatible; ntstatuscrawler.go; https://github.com/hillu/go-ntdll)")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalf("Could not retrieve %s: %v", URL, err)
}
if res.StatusCode != 200 {
log.Fatalf("Expected HTTP status 200, got %s", res.Status)
}
doc, err := goquery.NewDocumentFromResponse(res)
if err != nil {
log.Fatalf("Could not parse document from %s: %v", URL, err)
}
// This is how the table with the status-codes looks like inside the HTML:
// <table Responsive="true" summary="table">
// <tr class="thead" Responsive="true"><th scope="col">
// <p>Return value/code</p>
// </th><th scope="col">
// <p>Description</p>
// </th></tr>
// <tr><td data-th="
 Return value/code
 ">
// <p>0x00000000</p>
// <p>STATUS_SUCCESS</p>
// </td><td data-th="
 Description
 ">
// <p>The operation completed successfully. </p>
// </td></tr>
// <tr><td data-th="
 Return value/code
 ">
// <p>0x00000000</p>
// <p>STATUS_WAIT_0</p>
// </td><td data-th="
 Description
 ">
// <p>The caller specified WaitAny for WaitType and one of
// the dispatcher objects in the Object array has been set to the signaled
// state.</p>
// </td></tr>
var n2s = map[uint64]string{}
var spaces = regexp.MustCompile(" *")
doc.Find("table tr").Each(func(i int, s *goquery.Selection) {
if i == 0 {
return // skip header
}
st := status{}
td1 := s.Find("td")
if td1 == nil {
log.Println("entry ", i, "empty td")
return
}
p := td1.Find("p").First()
v := strings.TrimSpace(p.Text())
if _, err = fmt.Sscanf(v, "0x%X", &st.val); err != nil {
log.Println("entry ", i, err)
return
}
st.name = p.Next().Text()
n2s[st.val] = st.name // Any duplicate gets overwritten!
desc := strings.Replace(td1.Next().Find("p").Text(), "\n", "", -1)
st.desc = spaces.ReplaceAllLiteralString(desc, " ")
list = append(list, st)
})
// Sort the list by status code.
sort.Sort(list)
// Write the header
code := &bytes.Buffer{}
code.WriteString("package ntdll\n")
code.WriteString("// Generated by github.com/hillu/go-ntdll/statuscrawler; DO NOT EDIT\n")
fmt.Fprintf(code, "// Extracted from %s, ", URL)
if docid, found := doc.Find(`meta[name="document_id"]`).Attr("content"); found {
fmt.Fprintf(code, "document_id=%s\n\n", docid)
} else if sitever, found := doc.Find(`meta[name="ms.sitever"]`).Attr("content"); found {
fmt.Fprintf(code, "ms.sitever=%s\n\n", sitever)
} else if datetime, found := doc.Find(`time[data-article-date]`).Attr("datetime"); found {
fmt.Fprintf(code, "datetime=%s\n\n", datetime)
} else {
log.Print("Could not find timestamp; using time.Now()")
fmt.Fprintf(code, "on %s\n\n", time.Now().Format(time.RFC822))
}
// Write the constants
code.WriteString("const (\n")
for i, s := range list {
fmt.Fprintf(code, "// %s\n", s.desc)
if i == 0 {
fmt.Fprintf(code, "%s NtStatus = 0x%08X\n", s.name, s.val)
} else {
fmt.Fprintf(code, "%s = 0x%08X\n", s.name, s.val)
}
}
code.WriteString(")\n\n")
n2s[0x00000000] = "STATUS_SUCCESS" // Fix the correspondence
// Write the reverse-lookup-table.
code.WriteString("var ntStatus2str = map[NtStatus]string{\n")
for _, status := range list {
if short, ok := n2s[status.val]; ok {
fmt.Fprintf(code, "0x%08X : \"%s\",\n", status.val, short)
delete(n2s, status.val) // Avoid duplicates.
}
}
code.WriteString("}\n\n")
// Write the Stringer method for NtStatus
code.WriteString(`
func (status NtStatus) String() string {
return ntStatus2str[status]
}
`)
out, err := format.Source(code.Bytes())
if err != nil {
log.Println(err)
log.Println(code.String())
}
if f, err := os.Create("ntstatus_generated.go"); err != nil {
log.Fatal(err)
} else if n, err := f.Write(out); err != nil {
log.Fatal(err)
} else if n != len(out) {
log.Fatal("output size mismatch")
} else {
f.Close()
}
}