Add script/print-markdown-links.go
diff --git a/go.mod b/go.mod
index 4ec9849..4671479 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,8 @@
module github.com/google/wuffs
go 1.12
+
+require (
+ github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+ gopkg.in/russross/blackfriday.v2 v2.0.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..2b9acb9
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+gopkg.in/russross/blackfriday.v2 v2.0.0 h1:+FlnIV8DSQnT7NZ43hcVKcdJdzZoeCmJj4Ql8gq5keA=
+gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI=
diff --git a/script/print-markdown-links.go b/script/print-markdown-links.go
new file mode 100644
index 0000000..9b40ed8
--- /dev/null
+++ b/script/print-markdown-links.go
@@ -0,0 +1,130 @@
+// Copyright 2019 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build ignore
+
+package main
+
+// print-markdown-links.go prints a sorted list of the link targets in the
+// given .md files (or directories containing .md files).
+//
+// Usage: go run print-markdown-links.go dirname0 filename1 etc
+//
+// For link targets that look like "/file/names" (as opposed to starting with
+// web URLs starting with "http:" or "https:"), they are preceded by a '+' or a
+// '-' depending on whether or not that file or directory exists, assuming that
+// this script is run in the root directory to resolve filename links that
+// start with a slash.
+//
+// Printing web URLs can be skipped entirely by passing -skiphttp
+//
+// For example, running:
+// go run script/print-markdown-links.go -skiphttp doc | grep "^-"
+// can find broken Markdown links in the documentation.
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "gopkg.in/russross/blackfriday.v2"
+)
+
+var skiphttp = flag.Bool("skiphttp", false, `skip "http:" or "https:" links`)
+
+func main() {
+ if err := main1(); err != nil {
+ os.Stderr.WriteString(err.Error() + "\n")
+ os.Exit(1)
+ }
+}
+
+func main1() error {
+ flag.Parse()
+ r := &renderer{
+ links: map[string]struct{}{},
+ }
+
+ for _, arg := range flag.Args() {
+ err := filepath.Walk(arg, func(path string, info os.FileInfo, walkErr error) error {
+ if walkErr != nil {
+ return walkErr
+ }
+ if info.IsDir() || !strings.HasSuffix(path, ".md") {
+ return nil
+ }
+ return visit(r, path)
+ })
+ if err != nil {
+ return err
+ }
+ }
+
+ sorted := make([]string, 0, len(r.links))
+ for k := range r.links {
+ sorted = append(sorted, k)
+ }
+ sort.Strings(sorted)
+
+ for _, s := range sorted {
+ mark := '-'
+ switch {
+ case strings.HasPrefix(s, "http:"), strings.HasPrefix(s, "https:"):
+ if *skiphttp {
+ continue
+ }
+ mark = ':'
+ case (len(s) > 0) && (s[0] == '/'):
+ filename := s[1:]
+ if i := strings.IndexByte(filename, '#'); i >= 0 {
+ filename = filename[:i]
+ }
+ if _, err := os.Stat(filename); err == nil {
+ mark = '+'
+ }
+ }
+ fmt.Printf("%c %s\n", mark, s)
+ }
+ return nil
+}
+
+func visit(r *renderer, filename string) error {
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+ blackfriday.Run(src, blackfriday.WithRenderer(r))
+ return nil
+}
+
+type renderer struct {
+ links map[string]struct{}
+}
+
+func (r *renderer) RenderHeader(w io.Writer, n *blackfriday.Node) {}
+func (r *renderer) RenderFooter(w io.Writer, n *blackfriday.Node) {}
+
+func (r *renderer) RenderNode(w io.Writer, n *blackfriday.Node, entering bool) blackfriday.WalkStatus {
+ if entering {
+ if dst := n.LinkData.Destination; len(dst) != 0 {
+ r.links[string(dst)] = struct{}{}
+ }
+ }
+ return blackfriday.GoToNext
+}