Kaynağa Gözat

vendor: revendor

Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
Lucas Servén Marín 5 yıl önce
ebeveyn
işleme
410a014daf

+ 1 - 0
go.mod

@@ -6,6 +6,7 @@ require (
 	github.com/PuerkitoBio/purell v1.1.1 // indirect
 	github.com/ant31/crd-validation v0.0.0-20180801212718-38f6a293f140
 	github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310
+	github.com/campoy/embedmd v1.0.0
 	github.com/containernetworking/cni v0.6.0
 	github.com/containernetworking/plugins v0.6.0
 	github.com/coreos/go-iptables v0.4.0

+ 2 - 0
go.sum

@@ -13,6 +13,8 @@ github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310 h1:t+qxR
 github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
+github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
 github.com/containernetworking/cni v0.6.0 h1:FXICGBZNMtdHlW65trpoHviHctQD3seWhRRcqp2hMOU=
 github.com/containernetworking/cni v0.6.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
 github.com/containernetworking/plugins v0.6.0 h1:bqPT7yYisnWs+FrtgY5/qLEB9QZ/6z11wMNCwSdzZm0=

+ 4 - 0
vendor/github.com/campoy/embedmd/.gitignore

@@ -0,0 +1,4 @@
+.vscode/
+*.test
+embed
+.DS_Store

+ 9 - 0
vendor/github.com/campoy/embedmd/.travis.yml

@@ -0,0 +1,9 @@
+language: go
+
+go:
+  - 1.7.x
+  - 1.8.x
+  - 1.x
+  - master
+script:
+  - go test ./...

+ 202 - 0
vendor/github.com/campoy/embedmd/LICENSE

@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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
+
+       http://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.
+

+ 152 - 0
vendor/github.com/campoy/embedmd/README.md

@@ -0,0 +1,152 @@
+[![Build Status](https://travis-ci.org/campoy/embedmd.svg)](https://travis-ci.org/campoy/embedmd) [![Go Report Card](https://goreportcard.com/badge/github.com/campoy/embedmd)](https://goreportcard.com/report/github.com/campoy/embedmd)
+
+
+# embedmd
+
+Are you tired of copy pasting your code into your `README.md` file, just to
+forget about it later on and have unsynced copies? Or even worse, code
+that does not even compile?
+
+Then `embedmd` is for you!
+
+`embedmd` embeds files or fractions of files into Markdown files. It does
+so by searching `embedmd` commands, which are a subset of the Markdown
+syntax for comments. This means they are invisible when Markdown is
+rendered, so they can be kept in the file as pointers to the origin of
+the embedded text.
+
+The command receives a list of Markdown files. If no list is given, the command
+reads from the standard input.
+
+The format of an `embedmd` command is:
+
+```Markdown
+[embedmd]:# (pathOrURL language /start regexp/ /end regexp/)
+```
+
+The embedded code will be extracted from the file at `pathOrURL`,
+which can either be a relative path to a file in the local file
+system (using always forward slashes as directory separator) or
+a URL starting with `http://` or `https://`.
+If the `pathOrURL` is a URL the tool will fetch the content in that URL.
+The embedded content starts at the first line that matches `/start regexp/`
+and finishes at the first line matching `/end regexp/`.
+
+Omitting the the second regular expression will embed only the piece of text
+that matches `/regexp/`:
+
+```Markdown
+[embedmd]:# (pathOrURL language /regexp/)
+```
+
+To embed the whole line matching a regular expression you can use:
+
+```Markdown
+[embedmd]:# (pathOrURL language /.*regexp.*/)
+```
+
+To embed from a point to the end you should use:
+
+```Markdown
+[embedmd]:# (pathOrURL language /start regexp/ $)
+```
+
+To embed a whole file, omit both regular expressions:
+
+```Markdown
+[embedmd]:# (pathOrURL language)
+```
+
+You can omit the language in any of the previous commands, and the extension
+of the file will be used for the snippet syntax highlighting.
+
+This works when the file extensions matches the name of the language (like Go
+files, since `.go` matches `go`). However, this will fail with other files like
+`.md` whose language name is `markdown`.
+
+```Markdown
+[embedmd]:# (file.ext)
+```
+
+## Installation
+
+> You can install Go by following [these instructions](https://golang.org/doc/install).
+
+`embedmd` is written in Go, so if you have Go installed you can install it with
+`go get`:
+
+```
+go get github.com/campoy/embedmd
+```
+
+This will download the code, compile it, and leave an `embedmd` binary
+in `$GOPATH/bin`.
+
+Eventually, and if there's enough interest, I will provide binaries for
+every OS and architecture out there ... _eventually_.
+
+## Usage:
+
+Given the two files in [sample](sample):
+
+*hello.go:*
+
+[embedmd]:# (sample/hello.go)
+```go
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"fmt"
+	"time"
+)
+
+func main() {
+	fmt.Println("Hello, there, it is", time.Now())
+}
+```
+
+*docs.md:*
+
+[embedmd]:# (sample/docs.md Markdown /./ /embedmd.*time.*/)
+```Markdown
+# A hello world in Go
+
+Go is very simple, here you can see a whole "hello, world" program.
+
+[embedmd]:# (hello.go)
+
+We can try to embed a file from a directory.
+
+[embedmd]:# (test/hello.go /func main/ $)
+
+You always start with a `package` statement like:
+
+[embedmd]:# (hello.go /package.*/)
+
+Followed by an `import` statement:
+
+[embedmd]:# (hello.go /import/ /\)/)
+
+You can also see how to get the current time:
+
+[embedmd]:# (hello.go /time\.[^)]*\)/)
+```
+
+# Flags
+
+* `-w`: Executing `embedmd -w docs.md` will modify `docs.md`
+and add the corresponding code snippets, as shown in
+[sample/result.md](sample/result.md).
+
+* `-d`: Executing `embedmd -d docs.md` will display the difference
+between the contents of `docs.md` and the output of
+`embedmd docs.md`.
+
+### Disclaimer
+
+This is not an official Google product (experimental or otherwise), it is just
+code that happens to be owned by Google.

+ 101 - 0
vendor/github.com/campoy/embedmd/embedmd/command.go

@@ -0,0 +1,101 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// 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
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to writing, software distributed
+// under the License is distributed on a "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.
+
+package embedmd
+
+import (
+	"errors"
+	"path/filepath"
+	"strings"
+)
+
+type command struct {
+	path, lang string
+	start, end *string
+}
+
+func parseCommand(s string) (*command, error) {
+	s = strings.TrimSpace(s)
+	if len(s) < 2 || s[0] != '(' || s[len(s)-1] != ')' {
+		return nil, errors.New("argument list should be in parenthesis")
+	}
+
+	args, err := fields(s[1 : len(s)-1])
+	if err != nil {
+		return nil, err
+	}
+	if len(args) == 0 {
+		return nil, errors.New("missing file name")
+	}
+
+	cmd := &command{path: args[0]}
+	args = args[1:]
+	if len(args) > 0 && args[0][0] != '/' {
+		cmd.lang, args = args[0], args[1:]
+	} else {
+		ext := filepath.Ext(cmd.path[1:])
+		if len(ext) == 0 {
+			return nil, errors.New("language is required when file has no extension")
+		}
+		cmd.lang = ext[1:]
+	}
+
+	switch {
+	case len(args) == 1:
+		cmd.start = &args[0]
+	case len(args) == 2:
+		cmd.start, cmd.end = &args[0], &args[1]
+	case len(args) > 2:
+		return nil, errors.New("too many arguments")
+	}
+
+	return cmd, nil
+}
+
+// fields returns a list of the groups of text separated by blanks,
+// keeping all text surrounded by / as a group.
+func fields(s string) ([]string, error) {
+	var args []string
+
+	for s = strings.TrimSpace(s); len(s) > 0; s = strings.TrimSpace(s) {
+		if s[0] == '/' {
+			sep := nextSlash(s[1:])
+			if sep < 0 {
+				return nil, errors.New("unbalanced /")
+			}
+			args, s = append(args, s[:sep+2]), s[sep+2:]
+		} else {
+			sep := strings.IndexByte(s[1:], ' ')
+			if sep < 0 {
+				return append(args, s), nil
+			}
+			args, s = append(args, s[:sep+1]), s[sep+1:]
+		}
+	}
+
+	return args, nil
+}
+
+// nextSlash will find the index of the next unescaped slash in a string.
+func nextSlash(s string) int {
+	for sep := 0; ; sep++ {
+		i := strings.IndexByte(s[sep:], '/')
+		if i < 0 {
+			return -1
+		}
+		sep += i
+		if sep == 0 || s[sep-1] != '\\' {
+			return sep
+		}
+	}
+}

+ 51 - 0
vendor/github.com/campoy/embedmd/embedmd/content.go

@@ -0,0 +1,51 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// 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
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to writing, software distributed
+// under the License is distributed on a "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.
+
+package embedmd
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"path/filepath"
+	"strings"
+)
+
+// Fetcher provides an abstraction on a file system.
+// The Fetch function is called anytime some content needs to be fetched.
+// For now this includes files and URLs.
+// The first parameter is the base directory that could be used to resolve
+// relative paths. This base directory will be ignored for absolute paths,
+// such as URLs.
+type Fetcher interface {
+	Fetch(dir, path string) ([]byte, error)
+}
+
+type fetcher struct{}
+
+func (fetcher) Fetch(dir, path string) ([]byte, error) {
+	if !strings.HasPrefix(path, "http://") && !strings.HasPrefix(path, "https://") {
+		path = filepath.Join(dir, filepath.FromSlash(path))
+		return ioutil.ReadFile(path)
+	}
+
+	res, err := http.Get(path)
+	if err != nil {
+		return nil, err
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("status %s", res.Status)
+	}
+	return ioutil.ReadAll(res.Body)
+}

+ 153 - 0
vendor/github.com/campoy/embedmd/embedmd/embedmd.go

@@ -0,0 +1,153 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// 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
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to writing, software distributed
+// under the License is distributed on a "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.
+
+// Package embedmd provides a single function, Process, that parses markdown
+// searching for markdown comments.
+//
+// The format of an embedmd command is:
+//
+//     [embedmd]:# (pathOrURL language /start regexp/ /end regexp/)
+//
+// The embedded code will be extracted from the file at pathOrURL,
+// which can either be a relative path to a file in the local file
+// system (using always forward slashes as directory separator) or
+// a url starting with http:// or https://.
+// If the pathOrURL is a url the tool will fetch the content in that url.
+// The embedded content starts at the first line that matches /start regexp/
+// and finishes at the first line matching /end regexp/.
+//
+// Omitting the the second regular expression will embed only the piece of
+// text that matches /regexp/:
+//
+//     [embedmd]:# (pathOrURL language /regexp/)
+//
+// To embed the whole line matching a regular expression you can use:
+//
+//     [embedmd]:# (pathOrURL language /.*regexp.*\n/)
+//
+// If you want to embed from a point to the end you should use:
+//
+//     [embedmd]:# (pathOrURL language /start regexp/ $)
+//
+// Finally you can embed a whole file by omitting both regular expressions:
+//
+//     [embedmd]:# (pathOrURL language)
+//
+// You can ommit the language in any of the previous commands, and the extension
+// of the file will be used for the snippet syntax highlighting. Note that while
+// this works Go files, since the file extension .go matches the name of the language
+// go, this will fail with other files like .md whose language name is markdown.
+//
+//     [embedmd]:# (file.ext)
+//
+package embedmd
+
+import (
+	"fmt"
+	"io"
+	"regexp"
+)
+
+// Process reads markdown from the given io.Reader searching for an embedmd
+// command. When a command is found, it is executed and the output is written
+// into the given io.Writer with the rest of standard markdown.
+func Process(out io.Writer, in io.Reader, opts ...Option) error {
+	e := embedder{Fetcher: fetcher{}}
+	for _, opt := range opts {
+		opt.f(&e)
+	}
+	return process(out, in, e.runCommand)
+}
+
+// An Option provides a way to adapt the Process function to your needs.
+type Option struct{ f func(*embedder) }
+
+// WithBaseDir indicates that the given path should be used to resolve relative
+// paths.
+func WithBaseDir(path string) Option {
+	return Option{func(e *embedder) { e.baseDir = path }}
+}
+
+// WithFetcher provides a custom Fetcher to be used whenever a path or url needs
+// to be fetched.
+func WithFetcher(c Fetcher) Option {
+	return Option{func(e *embedder) { e.Fetcher = c }}
+}
+
+type embedder struct {
+	Fetcher
+	baseDir string
+}
+
+func (e *embedder) runCommand(w io.Writer, cmd *command) error {
+	b, err := e.Fetch(e.baseDir, cmd.path)
+	if err != nil {
+		return fmt.Errorf("could not read %s: %v", cmd.path, err)
+	}
+
+	b, err = extract(b, cmd.start, cmd.end)
+	if err != nil {
+		return fmt.Errorf("could not extract content from %s: %v", cmd.path, err)
+	}
+
+	if len(b) > 0 && b[len(b)-1] != '\n' {
+		b = append(b, '\n')
+	}
+
+	fmt.Fprintln(w, "```"+cmd.lang)
+	w.Write(b)
+	fmt.Fprintln(w, "```")
+	return nil
+}
+
+func extract(b []byte, start, end *string) ([]byte, error) {
+	if start == nil && end == nil {
+		return b, nil
+	}
+
+	match := func(s string) ([]int, error) {
+		if len(s) <= 2 || s[0] != '/' || s[len(s)-1] != '/' {
+			return nil, fmt.Errorf("missing slashes (/) around %q", s)
+		}
+		re, err := regexp.CompilePOSIX(s[1 : len(s)-1])
+		if err != nil {
+			return nil, err
+		}
+		loc := re.FindIndex(b)
+		if loc == nil {
+			return nil, fmt.Errorf("could not match %q", s)
+		}
+		return loc, nil
+	}
+
+	if *start != "" {
+		loc, err := match(*start)
+		if err != nil {
+			return nil, err
+		}
+		if end == nil {
+			return b[loc[0]:loc[1]], nil
+		}
+		b = b[loc[0]:]
+	}
+
+	if *end != "$" {
+		loc, err := match(*end)
+		if err != nil {
+			return nil, err
+		}
+		b = b[:loc[1]]
+	}
+
+	return b, nil
+}

+ 117 - 0
vendor/github.com/campoy/embedmd/embedmd/parser.go

@@ -0,0 +1,117 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// 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
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to writing, software distributed
+// under the License is distributed on a "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.
+
+package embedmd
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"strings"
+)
+
+type commandRunner func(io.Writer, *command) error
+
+func process(out io.Writer, in io.Reader, run commandRunner) error {
+	s := &countingScanner{bufio.NewScanner(in), 0}
+
+	state := parsingText
+	var err error
+	for state != nil {
+		state, err = state(out, s, run)
+		if err != nil {
+			return fmt.Errorf("%d: %v", s.line, err)
+		}
+	}
+
+	if err := s.Err(); err != nil {
+		return fmt.Errorf("%d: %v", s.line, err)
+	}
+	return nil
+}
+
+type countingScanner struct {
+	*bufio.Scanner
+	line int
+}
+
+func (c *countingScanner) Scan() bool {
+	b := c.Scanner.Scan()
+	if b {
+		c.line++
+	}
+	return b
+}
+
+type textScanner interface {
+	Text() string
+	Scan() bool
+}
+
+type state func(io.Writer, textScanner, commandRunner) (state, error)
+
+func parsingText(out io.Writer, s textScanner, run commandRunner) (state, error) {
+	if !s.Scan() {
+		return nil, nil // end of file, which is fine.
+	}
+	switch line := s.Text(); {
+	case strings.HasPrefix(line, "[embedmd]:#"):
+		return parsingCmd, nil
+	case strings.HasPrefix(line, "```"):
+		return codeParser{print: true}.parse, nil
+	default:
+		fmt.Fprintln(out, s.Text())
+		return parsingText, nil
+	}
+}
+
+func parsingCmd(out io.Writer, s textScanner, run commandRunner) (state, error) {
+	line := s.Text()
+	fmt.Fprintln(out, line)
+	args := line[strings.Index(line, "#")+1:]
+	cmd, err := parseCommand(args)
+	if err != nil {
+		return nil, err
+	}
+	if err := run(out, cmd); err != nil {
+		return nil, err
+	}
+	if !s.Scan() {
+		return nil, nil // end of file, which is fine.
+	}
+	if strings.HasPrefix(s.Text(), "```") {
+		return codeParser{print: false}.parse, nil
+	}
+	fmt.Fprintln(out, s.Text())
+	return parsingText, nil
+}
+
+type codeParser struct{ print bool }
+
+func (c codeParser) parse(out io.Writer, s textScanner, run commandRunner) (state, error) {
+	if c.print {
+		fmt.Fprintln(out, s.Text())
+	}
+	if !s.Scan() {
+		return nil, fmt.Errorf("unbalanced code section")
+	}
+	if !strings.HasPrefix(s.Text(), "```") {
+		return c.parse, nil
+	}
+
+	// print the end of the code section if needed and go back to parsing text.
+	if c.print {
+		fmt.Fprintln(out, s.Text())
+	}
+	return parsingText, nil
+}

+ 3 - 0
vendor/github.com/campoy/embedmd/go.mod

@@ -0,0 +1,3 @@
+module github.com/campoy/embedmd
+
+require github.com/pmezard/go-difflib v1.0.0

+ 2 - 0
vendor/github.com/campoy/embedmd/go.sum

@@ -0,0 +1,2 @@
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

+ 185 - 0
vendor/github.com/campoy/embedmd/main.go

@@ -0,0 +1,185 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// 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
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to writing, software distributed
+// under the License is distributed on a "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.
+
+// embedmd
+//
+// embedmd embeds files or fractions of files into markdown files.
+// It does so by searching embedmd commands, which are a subset of the
+// markdown syntax for comments. This means they are invisible when
+// markdown is rendered, so they can be kept in the file as pointers
+// to the origin of the embedded text.
+//
+// The command receives a list of markdown files, if none is given it
+// reads from the standard input.
+//
+// embedmd supports two flags:
+// -d: will print the difference of the input file with what the output
+//     would have been if executed.
+// -w: rewrites the given files rather than writing the output to the standard
+//     output.
+//
+// For more information on the format of the commands, read the documentation
+// of the github.com/campoy/embedmd/embedmd package.
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	"github.com/campoy/embedmd/embedmd"
+	"github.com/pmezard/go-difflib/difflib"
+)
+
+// modified while building by -ldflags.
+var version = "unkown"
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: embedmd [flags] [path ...]\n")
+	flag.PrintDefaults()
+}
+
+func main() {
+	rewrite := flag.Bool("w", false, "write result to (markdown) file instead of stdout")
+	doDiff := flag.Bool("d", false, "display diffs instead of rewriting files")
+	printVersion := flag.Bool("v", false, "display embedmd version")
+	flag.Usage = usage
+	flag.Parse()
+
+	if *printVersion {
+		fmt.Println("embedmd version: " + version)
+		return
+	}
+
+	diff, err := embed(flag.Args(), *rewrite, *doDiff)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(2)
+	}
+	if diff && *doDiff {
+		os.Exit(2)
+	}
+}
+
+var (
+	stdout io.Writer = os.Stdout
+	stdin  io.Reader = os.Stdin
+)
+
+func embed(paths []string, rewrite, doDiff bool) (foundDiff bool, err error) {
+	if rewrite && doDiff {
+		return false, fmt.Errorf("error: cannot use -w and -d simultaneously")
+	}
+
+	if len(paths) == 0 {
+		if rewrite {
+			return false, fmt.Errorf("error: cannot use -w with standard input")
+		}
+		if !doDiff {
+			return false, embedmd.Process(stdout, stdin)
+		}
+
+		var out, in bytes.Buffer
+		if err := embedmd.Process(&out, io.TeeReader(stdin, &in)); err != nil {
+			return false, err
+		}
+		d, err := diff(in.String(), out.String())
+		if err != nil || len(d) == 0 {
+			return false, err
+		}
+		fmt.Fprintf(stdout, "%s", d)
+		return true, nil
+	}
+
+	for _, path := range paths {
+		d, err := processFile(path, rewrite, doDiff)
+		if err != nil {
+			return false, fmt.Errorf("%s:%v", path, err)
+		}
+		foundDiff = foundDiff || d
+	}
+	return foundDiff, nil
+}
+
+type file interface {
+	io.ReadCloser
+	io.WriterAt
+	Truncate(int64) error
+}
+
+// replaced by testing functions.
+var openFile = func(name string) (file, error) {
+	return os.OpenFile(name, os.O_RDWR, 0666)
+}
+
+func readFile(path string) ([]byte, error) {
+	f, err := openFile(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	return ioutil.ReadAll(f)
+}
+
+func processFile(path string, rewrite, doDiff bool) (foundDiff bool, err error) {
+	if filepath.Ext(path) != ".md" {
+		return false, fmt.Errorf("not a markdown file")
+	}
+
+	f, err := openFile(path)
+	if err != nil {
+		return false, err
+	}
+	defer f.Close()
+
+	buf := new(bytes.Buffer)
+	if err := embedmd.Process(buf, f, embedmd.WithBaseDir(filepath.Dir(path))); err != nil {
+		return false, err
+	}
+
+	if doDiff {
+		f, err := readFile(path)
+		if err != nil {
+			return false, fmt.Errorf("could not read %s for diff: %v", path, err)
+		}
+		data, err := diff(string(f), buf.String())
+		if err != nil || len(data) == 0 {
+			return false, err
+		}
+		fmt.Fprintf(stdout, "%s", data)
+		return true, nil
+	}
+
+	if rewrite {
+		n, err := f.WriteAt(buf.Bytes(), 0)
+		if err != nil {
+			return false, fmt.Errorf("could not write: %v", err)
+		}
+		return false, f.Truncate(int64(n))
+	}
+
+	io.Copy(stdout, buf)
+	return false, nil
+}
+
+func diff(a, b string) (string, error) {
+	return difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+		A:       difflib.SplitLines(a),
+		B:       difflib.SplitLines(b),
+		Context: 3,
+	})
+}

+ 27 - 0
vendor/github.com/pmezard/go-difflib/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2013, Patrick Mezard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+    The names of its contributors may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 772 - 0
vendor/github.com/pmezard/go-difflib/difflib/difflib.go

@@ -0,0 +1,772 @@
+// Package difflib is a partial port of Python difflib module.
+//
+// It provides tools to compare sequences of strings and generate textual diffs.
+//
+// The following class and functions have been ported:
+//
+// - SequenceMatcher
+//
+// - unified_diff
+//
+// - context_diff
+//
+// Getting unified diffs was the main goal of the port. Keep in mind this code
+// is mostly suitable to output text differences in a human friendly way, there
+// are no guarantees generated diffs are consumable by patch(1).
+package difflib
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"strings"
+)
+
+func min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+func max(a, b int) int {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+func calculateRatio(matches, length int) float64 {
+	if length > 0 {
+		return 2.0 * float64(matches) / float64(length)
+	}
+	return 1.0
+}
+
+type Match struct {
+	A    int
+	B    int
+	Size int
+}
+
+type OpCode struct {
+	Tag byte
+	I1  int
+	I2  int
+	J1  int
+	J2  int
+}
+
+// SequenceMatcher compares sequence of strings. The basic
+// algorithm predates, and is a little fancier than, an algorithm
+// published in the late 1980's by Ratcliff and Obershelp under the
+// hyperbolic name "gestalt pattern matching".  The basic idea is to find
+// the longest contiguous matching subsequence that contains no "junk"
+// elements (R-O doesn't address junk).  The same idea is then applied
+// recursively to the pieces of the sequences to the left and to the right
+// of the matching subsequence.  This does not yield minimal edit
+// sequences, but does tend to yield matches that "look right" to people.
+//
+// SequenceMatcher tries to compute a "human-friendly diff" between two
+// sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the
+// longest *contiguous* & junk-free matching subsequence.  That's what
+// catches peoples' eyes.  The Windows(tm) windiff has another interesting
+// notion, pairing up elements that appear uniquely in each sequence.
+// That, and the method here, appear to yield more intuitive difference
+// reports than does diff.  This method appears to be the least vulnerable
+// to synching up on blocks of "junk lines", though (like blank lines in
+// ordinary text files, or maybe "<P>" lines in HTML files).  That may be
+// because this is the only method of the 3 that has a *concept* of
+// "junk" <wink>.
+//
+// Timing:  Basic R-O is cubic time worst case and quadratic time expected
+// case.  SequenceMatcher is quadratic time for the worst case and has
+// expected-case behavior dependent in a complicated way on how many
+// elements the sequences have in common; best case time is linear.
+type SequenceMatcher struct {
+	a              []string
+	b              []string
+	b2j            map[string][]int
+	IsJunk         func(string) bool
+	autoJunk       bool
+	bJunk          map[string]struct{}
+	matchingBlocks []Match
+	fullBCount     map[string]int
+	bPopular       map[string]struct{}
+	opCodes        []OpCode
+}
+
+func NewMatcher(a, b []string) *SequenceMatcher {
+	m := SequenceMatcher{autoJunk: true}
+	m.SetSeqs(a, b)
+	return &m
+}
+
+func NewMatcherWithJunk(a, b []string, autoJunk bool,
+	isJunk func(string) bool) *SequenceMatcher {
+
+	m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
+	m.SetSeqs(a, b)
+	return &m
+}
+
+// Set two sequences to be compared.
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
+	m.SetSeq1(a)
+	m.SetSeq2(b)
+}
+
+// Set the first sequence to be compared. The second sequence to be compared is
+// not changed.
+//
+// SequenceMatcher computes and caches detailed information about the second
+// sequence, so if you want to compare one sequence S against many sequences,
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
+// sequences.
+//
+// See also SetSeqs() and SetSeq2().
+func (m *SequenceMatcher) SetSeq1(a []string) {
+	if &a == &m.a {
+		return
+	}
+	m.a = a
+	m.matchingBlocks = nil
+	m.opCodes = nil
+}
+
+// Set the second sequence to be compared. The first sequence to be compared is
+// not changed.
+func (m *SequenceMatcher) SetSeq2(b []string) {
+	if &b == &m.b {
+		return
+	}
+	m.b = b
+	m.matchingBlocks = nil
+	m.opCodes = nil
+	m.fullBCount = nil
+	m.chainB()
+}
+
+func (m *SequenceMatcher) chainB() {
+	// Populate line -> index mapping
+	b2j := map[string][]int{}
+	for i, s := range m.b {
+		indices := b2j[s]
+		indices = append(indices, i)
+		b2j[s] = indices
+	}
+
+	// Purge junk elements
+	m.bJunk = map[string]struct{}{}
+	if m.IsJunk != nil {
+		junk := m.bJunk
+		for s, _ := range b2j {
+			if m.IsJunk(s) {
+				junk[s] = struct{}{}
+			}
+		}
+		for s, _ := range junk {
+			delete(b2j, s)
+		}
+	}
+
+	// Purge remaining popular elements
+	popular := map[string]struct{}{}
+	n := len(m.b)
+	if m.autoJunk && n >= 200 {
+		ntest := n/100 + 1
+		for s, indices := range b2j {
+			if len(indices) > ntest {
+				popular[s] = struct{}{}
+			}
+		}
+		for s, _ := range popular {
+			delete(b2j, s)
+		}
+	}
+	m.bPopular = popular
+	m.b2j = b2j
+}
+
+func (m *SequenceMatcher) isBJunk(s string) bool {
+	_, ok := m.bJunk[s]
+	return ok
+}
+
+// Find longest matching block in a[alo:ahi] and b[blo:bhi].
+//
+// If IsJunk is not defined:
+//
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
+//     alo <= i <= i+k <= ahi
+//     blo <= j <= j+k <= bhi
+// and for all (i',j',k') meeting those conditions,
+//     k >= k'
+//     i <= i'
+//     and if i == i', j <= j'
+//
+// In other words, of all maximal matching blocks, return one that
+// starts earliest in a, and of all those maximal matching blocks that
+// start earliest in a, return the one that starts earliest in b.
+//
+// If IsJunk is defined, first the longest matching block is
+// determined as above, but with the additional restriction that no
+// junk element appears in the block.  Then that block is extended as
+// far as possible by matching (only) junk elements on both sides.  So
+// the resulting block never matches on junk except as identical junk
+// happens to be adjacent to an "interesting" match.
+//
+// If no blocks match, return (alo, blo, 0).
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
+	// CAUTION:  stripping common prefix or suffix would be incorrect.
+	// E.g.,
+	//    ab
+	//    acab
+	// Longest matching block is "ab", but if common prefix is
+	// stripped, it's "a" (tied with "b").  UNIX(tm) diff does so
+	// strip, so ends up claiming that ab is changed to acab by
+	// inserting "ca" in the middle.  That's minimal but unintuitive:
+	// "it's obvious" that someone inserted "ac" at the front.
+	// Windiff ends up at the same place as diff, but by pairing up
+	// the unique 'b's and then matching the first two 'a's.
+	besti, bestj, bestsize := alo, blo, 0
+
+	// find longest junk-free match
+	// during an iteration of the loop, j2len[j] = length of longest
+	// junk-free match ending with a[i-1] and b[j]
+	j2len := map[int]int{}
+	for i := alo; i != ahi; i++ {
+		// look at all instances of a[i] in b; note that because
+		// b2j has no junk keys, the loop is skipped if a[i] is junk
+		newj2len := map[int]int{}
+		for _, j := range m.b2j[m.a[i]] {
+			// a[i] matches b[j]
+			if j < blo {
+				continue
+			}
+			if j >= bhi {
+				break
+			}
+			k := j2len[j-1] + 1
+			newj2len[j] = k
+			if k > bestsize {
+				besti, bestj, bestsize = i-k+1, j-k+1, k
+			}
+		}
+		j2len = newj2len
+	}
+
+	// Extend the best by non-junk elements on each end.  In particular,
+	// "popular" non-junk elements aren't in b2j, which greatly speeds
+	// the inner loop above, but also means "the best" match so far
+	// doesn't contain any junk *or* popular non-junk elements.
+	for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
+		m.a[besti-1] == m.b[bestj-1] {
+		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+	}
+	for besti+bestsize < ahi && bestj+bestsize < bhi &&
+		!m.isBJunk(m.b[bestj+bestsize]) &&
+		m.a[besti+bestsize] == m.b[bestj+bestsize] {
+		bestsize += 1
+	}
+
+	// Now that we have a wholly interesting match (albeit possibly
+	// empty!), we may as well suck up the matching junk on each
+	// side of it too.  Can't think of a good reason not to, and it
+	// saves post-processing the (possibly considerable) expense of
+	// figuring out what to do with it.  In the case of an empty
+	// interesting match, this is clearly the right thing to do,
+	// because no other kind of match is possible in the regions.
+	for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
+		m.a[besti-1] == m.b[bestj-1] {
+		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+	}
+	for besti+bestsize < ahi && bestj+bestsize < bhi &&
+		m.isBJunk(m.b[bestj+bestsize]) &&
+		m.a[besti+bestsize] == m.b[bestj+bestsize] {
+		bestsize += 1
+	}
+
+	return Match{A: besti, B: bestj, Size: bestsize}
+}
+
+// Return list of triples describing matching subsequences.
+//
+// Each triple is of the form (i, j, n), and means that
+// a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
+// adjacent triples in the list, and the second is not the last triple in the
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
+// adjacent equal blocks.
+//
+// The last triple is a dummy, (len(a), len(b), 0), and is the only
+// triple with n==0.
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
+	if m.matchingBlocks != nil {
+		return m.matchingBlocks
+	}
+
+	var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
+	matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
+		match := m.findLongestMatch(alo, ahi, blo, bhi)
+		i, j, k := match.A, match.B, match.Size
+		if match.Size > 0 {
+			if alo < i && blo < j {
+				matched = matchBlocks(alo, i, blo, j, matched)
+			}
+			matched = append(matched, match)
+			if i+k < ahi && j+k < bhi {
+				matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
+			}
+		}
+		return matched
+	}
+	matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
+
+	// It's possible that we have adjacent equal blocks in the
+	// matching_blocks list now.
+	nonAdjacent := []Match{}
+	i1, j1, k1 := 0, 0, 0
+	for _, b := range matched {
+		// Is this block adjacent to i1, j1, k1?
+		i2, j2, k2 := b.A, b.B, b.Size
+		if i1+k1 == i2 && j1+k1 == j2 {
+			// Yes, so collapse them -- this just increases the length of
+			// the first block by the length of the second, and the first
+			// block so lengthened remains the block to compare against.
+			k1 += k2
+		} else {
+			// Not adjacent.  Remember the first block (k1==0 means it's
+			// the dummy we started with), and make the second block the
+			// new block to compare against.
+			if k1 > 0 {
+				nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+			}
+			i1, j1, k1 = i2, j2, k2
+		}
+	}
+	if k1 > 0 {
+		nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+	}
+
+	nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
+	m.matchingBlocks = nonAdjacent
+	return m.matchingBlocks
+}
+
+// Return list of 5-tuples describing how to turn a into b.
+//
+// Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
+// tuple preceding it, and likewise for j1 == the previous j2.
+//
+// The tags are characters, with these meanings:
+//
+// 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2]
+//
+// 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case.
+//
+// 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
+//
+// 'e' (equal):    a[i1:i2] == b[j1:j2]
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
+	if m.opCodes != nil {
+		return m.opCodes
+	}
+	i, j := 0, 0
+	matching := m.GetMatchingBlocks()
+	opCodes := make([]OpCode, 0, len(matching))
+	for _, m := range matching {
+		//  invariant:  we've pumped out correct diffs to change
+		//  a[:i] into b[:j], and the next matching block is
+		//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump
+		//  out a diff to change a[i:ai] into b[j:bj], pump out
+		//  the matching block, and move (i,j) beyond the match
+		ai, bj, size := m.A, m.B, m.Size
+		tag := byte(0)
+		if i < ai && j < bj {
+			tag = 'r'
+		} else if i < ai {
+			tag = 'd'
+		} else if j < bj {
+			tag = 'i'
+		}
+		if tag > 0 {
+			opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
+		}
+		i, j = ai+size, bj+size
+		// the list of matching blocks is terminated by a
+		// sentinel with size 0
+		if size > 0 {
+			opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
+		}
+	}
+	m.opCodes = opCodes
+	return m.opCodes
+}
+
+// Isolate change clusters by eliminating ranges with no changes.
+//
+// Return a generator of groups with up to n lines of context.
+// Each group is in the same format as returned by GetOpCodes().
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
+	if n < 0 {
+		n = 3
+	}
+	codes := m.GetOpCodes()
+	if len(codes) == 0 {
+		codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
+	}
+	// Fixup leading and trailing groups if they show no changes.
+	if codes[0].Tag == 'e' {
+		c := codes[0]
+		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+		codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
+	}
+	if codes[len(codes)-1].Tag == 'e' {
+		c := codes[len(codes)-1]
+		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+		codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
+	}
+	nn := n + n
+	groups := [][]OpCode{}
+	group := []OpCode{}
+	for _, c := range codes {
+		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+		// End the current group and start a new one whenever
+		// there is a large range with no changes.
+		if c.Tag == 'e' && i2-i1 > nn {
+			group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
+				j1, min(j2, j1+n)})
+			groups = append(groups, group)
+			group = []OpCode{}
+			i1, j1 = max(i1, i2-n), max(j1, j2-n)
+		}
+		group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
+	}
+	if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
+		groups = append(groups, group)
+	}
+	return groups
+}
+
+// Return a measure of the sequences' similarity (float in [0,1]).
+//
+// Where T is the total number of elements in both sequences, and
+// M is the number of matches, this is 2.0*M / T.
+// Note that this is 1 if the sequences are identical, and 0 if
+// they have nothing in common.
+//
+// .Ratio() is expensive to compute if you haven't already computed
+// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
+// want to try .QuickRatio() or .RealQuickRation() first to get an
+// upper bound.
+func (m *SequenceMatcher) Ratio() float64 {
+	matches := 0
+	for _, m := range m.GetMatchingBlocks() {
+		matches += m.Size
+	}
+	return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() relatively quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute.
+func (m *SequenceMatcher) QuickRatio() float64 {
+	// viewing a and b as multisets, set matches to the cardinality
+	// of their intersection; this counts the number of matches
+	// without regard to order, so is clearly an upper bound
+	if m.fullBCount == nil {
+		m.fullBCount = map[string]int{}
+		for _, s := range m.b {
+			m.fullBCount[s] = m.fullBCount[s] + 1
+		}
+	}
+
+	// avail[x] is the number of times x appears in 'b' less the
+	// number of times we've seen it in 'a' so far ... kinda
+	avail := map[string]int{}
+	matches := 0
+	for _, s := range m.a {
+		n, ok := avail[s]
+		if !ok {
+			n = m.fullBCount[s]
+		}
+		avail[s] = n - 1
+		if n > 0 {
+			matches += 1
+		}
+	}
+	return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() very quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute than either .Ratio() or .QuickRatio().
+func (m *SequenceMatcher) RealQuickRatio() float64 {
+	la, lb := len(m.a), len(m.b)
+	return calculateRatio(min(la, lb), la+lb)
+}
+
+// Convert range to the "ed" format
+func formatRangeUnified(start, stop int) string {
+	// Per the diff spec at http://www.unix.org/single_unix_specification/
+	beginning := start + 1 // lines start numbering with one
+	length := stop - start
+	if length == 1 {
+		return fmt.Sprintf("%d", beginning)
+	}
+	if length == 0 {
+		beginning -= 1 // empty ranges begin at line just before the range
+	}
+	return fmt.Sprintf("%d,%d", beginning, length)
+}
+
+// Unified diff parameters
+type UnifiedDiff struct {
+	A        []string // First sequence lines
+	FromFile string   // First file name
+	FromDate string   // First file time
+	B        []string // Second sequence lines
+	ToFile   string   // Second file name
+	ToDate   string   // Second file time
+	Eol      string   // Headers end of line, defaults to LF
+	Context  int      // Number of context lines
+}
+
+// Compare two sequences of lines; generate the delta as a unified diff.
+//
+// Unified diffs are a compact way of showing line changes and a few
+// lines of context.  The number of context lines is set by 'n' which
+// defaults to three.
+//
+// By default, the diff control lines (those with ---, +++, or @@) are
+// created with a trailing newline.  This is helpful so that inputs
+// created from file.readlines() result in diffs that are suitable for
+// file.writelines() since both the inputs and outputs have trailing
+// newlines.
+//
+// For inputs that do not have trailing newlines, set the lineterm
+// argument to "" so that the output will be uniformly newline free.
+//
+// The unidiff format normally has a header for filenames and modification
+// times.  Any or all of these may be specified using strings for
+// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
+// The modification times are normally expressed in the ISO 8601 format.
+func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
+	buf := bufio.NewWriter(writer)
+	defer buf.Flush()
+	wf := func(format string, args ...interface{}) error {
+		_, err := buf.WriteString(fmt.Sprintf(format, args...))
+		return err
+	}
+	ws := func(s string) error {
+		_, err := buf.WriteString(s)
+		return err
+	}
+
+	if len(diff.Eol) == 0 {
+		diff.Eol = "\n"
+	}
+
+	started := false
+	m := NewMatcher(diff.A, diff.B)
+	for _, g := range m.GetGroupedOpCodes(diff.Context) {
+		if !started {
+			started = true
+			fromDate := ""
+			if len(diff.FromDate) > 0 {
+				fromDate = "\t" + diff.FromDate
+			}
+			toDate := ""
+			if len(diff.ToDate) > 0 {
+				toDate = "\t" + diff.ToDate
+			}
+			if diff.FromFile != "" || diff.ToFile != "" {
+				err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
+				if err != nil {
+					return err
+				}
+				err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
+				if err != nil {
+					return err
+				}
+			}
+		}
+		first, last := g[0], g[len(g)-1]
+		range1 := formatRangeUnified(first.I1, last.I2)
+		range2 := formatRangeUnified(first.J1, last.J2)
+		if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
+			return err
+		}
+		for _, c := range g {
+			i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+			if c.Tag == 'e' {
+				for _, line := range diff.A[i1:i2] {
+					if err := ws(" " + line); err != nil {
+						return err
+					}
+				}
+				continue
+			}
+			if c.Tag == 'r' || c.Tag == 'd' {
+				for _, line := range diff.A[i1:i2] {
+					if err := ws("-" + line); err != nil {
+						return err
+					}
+				}
+			}
+			if c.Tag == 'r' || c.Tag == 'i' {
+				for _, line := range diff.B[j1:j2] {
+					if err := ws("+" + line); err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+// Like WriteUnifiedDiff but returns the diff a string.
+func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
+	w := &bytes.Buffer{}
+	err := WriteUnifiedDiff(w, diff)
+	return string(w.Bytes()), err
+}
+
+// Convert range to the "ed" format.
+func formatRangeContext(start, stop int) string {
+	// Per the diff spec at http://www.unix.org/single_unix_specification/
+	beginning := start + 1 // lines start numbering with one
+	length := stop - start
+	if length == 0 {
+		beginning -= 1 // empty ranges begin at line just before the range
+	}
+	if length <= 1 {
+		return fmt.Sprintf("%d", beginning)
+	}
+	return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
+}
+
+type ContextDiff UnifiedDiff
+
+// Compare two sequences of lines; generate the delta as a context diff.
+//
+// Context diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by diff.Context
+// which defaults to three.
+//
+// By default, the diff control lines (those with *** or ---) are
+// created with a trailing newline.
+//
+// For inputs that do not have trailing newlines, set the diff.Eol
+// argument to "" so that the output will be uniformly newline free.
+//
+// The context diff format normally has a header for filenames and
+// modification times.  Any or all of these may be specified using
+// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
+// The modification times are normally expressed in the ISO 8601 format.
+// If not specified, the strings default to blanks.
+func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
+	buf := bufio.NewWriter(writer)
+	defer buf.Flush()
+	var diffErr error
+	wf := func(format string, args ...interface{}) {
+		_, err := buf.WriteString(fmt.Sprintf(format, args...))
+		if diffErr == nil && err != nil {
+			diffErr = err
+		}
+	}
+	ws := func(s string) {
+		_, err := buf.WriteString(s)
+		if diffErr == nil && err != nil {
+			diffErr = err
+		}
+	}
+
+	if len(diff.Eol) == 0 {
+		diff.Eol = "\n"
+	}
+
+	prefix := map[byte]string{
+		'i': "+ ",
+		'd': "- ",
+		'r': "! ",
+		'e': "  ",
+	}
+
+	started := false
+	m := NewMatcher(diff.A, diff.B)
+	for _, g := range m.GetGroupedOpCodes(diff.Context) {
+		if !started {
+			started = true
+			fromDate := ""
+			if len(diff.FromDate) > 0 {
+				fromDate = "\t" + diff.FromDate
+			}
+			toDate := ""
+			if len(diff.ToDate) > 0 {
+				toDate = "\t" + diff.ToDate
+			}
+			if diff.FromFile != "" || diff.ToFile != "" {
+				wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
+				wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
+			}
+		}
+
+		first, last := g[0], g[len(g)-1]
+		ws("***************" + diff.Eol)
+
+		range1 := formatRangeContext(first.I1, last.I2)
+		wf("*** %s ****%s", range1, diff.Eol)
+		for _, c := range g {
+			if c.Tag == 'r' || c.Tag == 'd' {
+				for _, cc := range g {
+					if cc.Tag == 'i' {
+						continue
+					}
+					for _, line := range diff.A[cc.I1:cc.I2] {
+						ws(prefix[cc.Tag] + line)
+					}
+				}
+				break
+			}
+		}
+
+		range2 := formatRangeContext(first.J1, last.J2)
+		wf("--- %s ----%s", range2, diff.Eol)
+		for _, c := range g {
+			if c.Tag == 'r' || c.Tag == 'i' {
+				for _, cc := range g {
+					if cc.Tag == 'd' {
+						continue
+					}
+					for _, line := range diff.B[cc.J1:cc.J2] {
+						ws(prefix[cc.Tag] + line)
+					}
+				}
+				break
+			}
+		}
+	}
+	return diffErr
+}
+
+// Like WriteContextDiff but returns the diff a string.
+func GetContextDiffString(diff ContextDiff) (string, error) {
+	w := &bytes.Buffer{}
+	err := WriteContextDiff(w, diff)
+	return string(w.Bytes()), err
+}
+
+// Split a string on "\n" while preserving them. The output can be used
+// as input for UnifiedDiff and ContextDiff structures.
+func SplitLines(s string) []string {
+	lines := strings.SplitAfter(s, "\n")
+	lines[len(lines)-1] += "\n"
+	return lines
+}

+ 6 - 0
vendor/modules.txt

@@ -16,6 +16,10 @@ github.com/awalterschulze/gographviz/internal/parser
 github.com/awalterschulze/gographviz/internal/token
 # github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973
 github.com/beorn7/perks/quantile
+# github.com/campoy/embedmd v1.0.0
+## explicit
+github.com/campoy/embedmd
+github.com/campoy/embedmd/embedmd
 # github.com/containernetworking/cni v0.6.0
 ## explicit
 github.com/containernetworking/cni/libcni
@@ -128,6 +132,8 @@ github.com/oklog/run
 ## explicit
 # github.com/onsi/gomega v1.5.0
 ## explicit
+# github.com/pmezard/go-difflib v1.0.0
+github.com/pmezard/go-difflib/difflib
 # github.com/prometheus/client_golang v0.9.2
 ## explicit
 github.com/prometheus/client_golang/prometheus