-
Notifications
You must be signed in to change notification settings - Fork 22
/
main.go
116 lines (101 loc) · 2.42 KB
/
main.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
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"regexp"
"strings"
"github.com/nickng/bibtex"
)
// Matches e.g.: @inproceedings{Müller2024a,
// \p{L}\p{M} matches any letter, including accented characters.
var re = regexp.MustCompile(`@[a-z]*\{([\"\p{L}\p{M}\-]*[0-9]{4}[a-z]),`)
// Map a cite name (e.g., Doe2024a) to its line number in the .bib file. All
// cite names are unique.
type entryToLineFunc func(string) int
// Augment bibtex.BibEntry with the entry's line number in the .bib file.
type bibEntry struct {
bibtex.BibEntry
lineNum int
}
func toStr(b bibtex.BibString) string {
if b == nil {
return ""
}
return b.String()
}
func parseBibFile(path string) []bibEntry {
file, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
bib, err := bibtex.Parse(file)
if err != nil {
log.Fatal(err)
}
// Augment our BibTeX entries with their respective line numbers in the .bib
// file. This is necessary to create the "Download BibTeX" links.
lineOf := buildEntryToLineFunc(path)
bibEntries := []bibEntry{}
for _, entry := range bib.Entries {
bibEntries = append(bibEntries, bibEntry{
BibEntry: *entry,
lineNum: lineOf(entry.CiteName),
})
}
return bibEntries
}
func buildEntryToLineFunc(path string) entryToLineFunc {
file, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
sc := bufio.NewScanner(file)
entryToLine := make(map[string]int)
line := 0
for sc.Scan() {
line++
s := sc.Text()
if !strings.HasPrefix(s, "@") {
continue
}
entry := parseCiteName(s) // E.g., Doe2024a
entryToLine[entry] = line
}
if err := sc.Err(); err != nil {
log.Fatalf("scan file error: %v", err)
}
return func(entry string) int {
if line, ok := entryToLine[entry]; ok {
return line
}
log.Fatalf("could not find line number for cite name: %s", entry)
return -1
}
}
func parseCiteName(line string) string {
matches := re.FindStringSubmatch(line)
if len(matches) != 2 {
log.Fatalf("failed to extract cite name of: %s", line)
}
return matches[1]
}
func run(w io.Writer, bibEntries []bibEntry) {
fmt.Fprint(w, header())
fmt.Fprintln(w, "<div id='container'>")
makeBib(w, bibEntries)
fmt.Fprintln(w, "</div>")
fmt.Fprint(w, footer())
}
func main() {
path := flag.String("path", "", "Path to .bib file.")
flag.Parse()
if *path == "" {
log.Fatal("No path to .bib file provided.")
}
run(os.Stdout, parseBibFile(*path))
log.Println("Successfully created bibliography.")
}