summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2017-08-08 11:49:45 +0200
committerDimitri Sokolyuk <demon@dim13.org>2017-08-08 11:49:45 +0200
commit9e2db6a2e70494e349153091b1a4168b0e5e88a8 (patch)
tree2adc8e4359f490f38b003489122c815c11a7f85f
parentf2433d300721bd5bb3c63715661fc88b1863ff26 (diff)
Vendor
-rw-r--r--Gopkg.lock21
-rw-r--r--Gopkg.toml75
-rw-r--r--metrics.go12
-rw-r--r--vendor/github.com/sarnowski/mitigation/LICENSE13
-rw-r--r--vendor/github.com/sarnowski/mitigation/README50
-rw-r--r--vendor/github.com/sarnowski/mitigation/mitigation.go118
-rw-r--r--vendor/github.com/sarnowski/mitigation/mitigation_test.go173
-rw-r--r--vendor/golang.org/x/tools/.gitattributes10
-rw-r--r--vendor/golang.org/x/tools/.gitignore2
-rw-r--r--vendor/golang.org/x/tools/AUTHORS3
-rw-r--r--vendor/golang.org/x/tools/CONTRIBUTING.md31
-rw-r--r--vendor/golang.org/x/tools/CONTRIBUTORS3
-rw-r--r--vendor/golang.org/x/tools/LICENSE27
-rw-r--r--vendor/golang.org/x/tools/PATENTS22
-rw-r--r--vendor/golang.org/x/tools/README10
-rw-r--r--vendor/golang.org/x/tools/blog/atom/atom.go61
-rw-r--r--vendor/golang.org/x/tools/blog/blog.go424
-rw-r--r--vendor/golang.org/x/tools/codereview.cfg1
-rw-r--r--vendor/golang.org/x/tools/present/args.go229
-rw-r--r--vendor/golang.org/x/tools/present/caption.go22
-rw-r--r--vendor/golang.org/x/tools/present/code.go267
-rw-r--r--vendor/golang.org/x/tools/present/code_test.go225
-rw-r--r--vendor/golang.org/x/tools/present/doc.go266
-rw-r--r--vendor/golang.org/x/tools/present/html.go31
-rw-r--r--vendor/golang.org/x/tools/present/iframe.go45
-rw-r--r--vendor/golang.org/x/tools/present/image.go50
-rw-r--r--vendor/golang.org/x/tools/present/link.go100
-rw-r--r--vendor/golang.org/x/tools/present/link_test.go40
-rw-r--r--vendor/golang.org/x/tools/present/parse.go559
-rw-r--r--vendor/golang.org/x/tools/present/style.go167
-rw-r--r--vendor/golang.org/x/tools/present/style_test.go124
-rw-r--r--vendor/golang.org/x/tools/present/video.go51
32 files changed, 3220 insertions, 12 deletions
diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 0000000..18e1c70
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,21 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ name = "github.com/sarnowski/mitigation"
+ packages = ["."]
+ revision = "bff64e7420fb68c0efa9e92548d4538deed75ca2"
+ version = "1.0.3"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/tools"
+ packages = ["blog","blog/atom","present"]
+ revision = "f03b3350b7d6cb09e4221e7f2ecffaf41f89fce1"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "eeff4fecf2a6d695a64109ea2b34350fc4712d1f3a8f5b1e82b5ac8368664a73"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 0000000..1937368
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,75 @@
+
+## Gopkg.toml example (these lines may be deleted)
+
+## "metadata" defines metadata about the project that could be used by other independent
+## systems. The metadata defined here will be ignored by dep.
+# [metadata]
+# key1 = "value that convey data to other systems"
+# system1-data = "value that is used by a system"
+# system2-data = "value that is used by another system"
+
+## "required" lists a set of packages (not projects) that must be included in
+## Gopkg.lock. This list is merged with the set of packages imported by the current
+## project. Use it when your project needs a package it doesn't explicitly import -
+## including "main" packages.
+# required = ["github.com/user/thing/cmd/thing"]
+
+## "ignored" lists a set of packages (not projects) that are ignored when
+## dep statically analyzes source code. Ignored packages can be in this project,
+## or in a dependency.
+# ignored = ["github.com/user/project/badpkg"]
+
+## Constraints are rules for how directly imported projects
+## may be incorporated into the depgraph. They are respected by
+## dep whether coming from the Gopkg.toml of the current project or a dependency.
+# [[constraint]]
+## Required: the root import path of the project being constrained.
+# name = "github.com/user/project"
+#
+## Recommended: the version constraint to enforce for the project.
+## Only one of "branch", "version" or "revision" can be specified.
+# version = "1.0.0"
+# branch = "master"
+# revision = "abc123"
+#
+## Optional: an alternate location (URL or import path) for the project's source.
+# source = "https://github.com/myfork/package.git"
+#
+## "metadata" defines metadata about the dependency or override that could be used
+## by other independent systems. The metadata defined here will be ignored by dep.
+# [metadata]
+# key1 = "value that convey data to other systems"
+# system1-data = "value that is used by a system"
+# system2-data = "value that is used by another system"
+
+## Overrides have the same structure as [[constraint]], but supersede all
+## [[constraint]] declarations from all projects. Only [[override]] from
+## the current project's are applied.
+##
+## Overrides are a sledgehammer. Use them only as a last resort.
+# [[override]]
+## Required: the root import path of the project being constrained.
+# name = "github.com/user/project"
+#
+## Optional: specifying a version constraint override will cause all other
+## constraints on this project to be ignored; only the overridden constraint
+## need be satisfied.
+## Again, only one of "branch", "version" or "revision" can be specified.
+# version = "1.0.0"
+# branch = "master"
+# revision = "abc123"
+#
+## Optional: specifying an alternate source location as an override will
+## enforce that the alternate location is used for that project, regardless of
+## what source location any dependent projects specify.
+# source = "https://github.com/myfork/package.git"
+
+
+
+[[constraint]]
+ name = "github.com/sarnowski/mitigation"
+ version = "1.0.3"
+
+[[constraint]]
+ branch = "master"
+ name = "golang.org/x/tools"
diff --git a/metrics.go b/metrics.go
deleted file mode 100644
index 333a9fd..0000000
--- a/metrics.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package main
-
-import (
- "net/http"
- _ "net/http/pprof"
-
- "github.com/prometheus/client_golang/prometheus"
-)
-
-func init() {
- http.Handle("/metrics", prometheus.Handler())
-}
diff --git a/vendor/github.com/sarnowski/mitigation/LICENSE b/vendor/github.com/sarnowski/mitigation/LICENSE
new file mode 100644
index 0000000..1c949b5
--- /dev/null
+++ b/vendor/github.com/sarnowski/mitigation/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2012 Tobias Sarnowski <tobias@trustedco.de>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/vendor/github.com/sarnowski/mitigation/README b/vendor/github.com/sarnowski/mitigation/README
new file mode 100644
index 0000000..e7779eb
--- /dev/null
+++ b/vendor/github.com/sarnowski/mitigation/README
@@ -0,0 +1,50 @@
+mitigation for go applications
+=============================================================================
+
+Package mitigation provides the possibility to prevent damage caused by bugs
+or exploits.
+
+
+Techniques
+-----------------------------------------------------------------------------
+
+The package uses multiple techniques to mitigate damage:
+
+ - privilege revocation: switch to an unprivileged user
+ - chroot jail: restrict filesystem access
+ - defined environment: reset all environment variables
+
+Enables the ability to implement system-supported privilege seperation.
+
+
+Installation
+-----------------------------------------------------------------------------
+
+To install and use the mitigation execute the following command:
+
+ go get github.com/sarnowski/mitigation
+
+
+Documentation
+-----------------------------------------------------------------------------
+
+Documentation is provided in the go way and is accessible through "godoc".
+You will find explanation, requirements and examples.
+
+
+License
+-----------------------------------------------------------------------------
+
+Copyright (c) 2012 Tobias Sarnowski <tobias@trustedco.de>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/vendor/github.com/sarnowski/mitigation/mitigation.go b/vendor/github.com/sarnowski/mitigation/mitigation.go
new file mode 100644
index 0000000..a1110e4
--- /dev/null
+++ b/vendor/github.com/sarnowski/mitigation/mitigation.go
@@ -0,0 +1,118 @@
+/*
+Package mitigation provides the possibility to prevent damage through bugs or exploits.
+
+The package uses multiple techniques to mitigate damage:
+ - privilege revocation: switch to an unprivileged user
+ - chroot jail: restrict filesystem access
+ - defined environment: reset all environment variables
+
+The following prerequisites are nessecary:
+ - The application must run as root
+ - You need to provide a valid user id
+ - You need to provide a valid group id
+ - You need to provide an existing path
+
+Activate() will not return any error. It will panic as soon as anything
+goes wrong because there is no good way to recover. To provide a sensible
+fallback you can use the CanActivate() function.
+
+WARNING: Windows is not supported. Windows has no equivalents for the used
+techniques.
+
+WARNING: Linux is not POSIX compatible and therefor setuid() only changes the
+user ID of the current thread. At the time, there is no way to safely use
+this within go as there may already be other threads spawned at the time
+this library is called. More about this issue here:
+ http://code.google.com/p/go/issues/detail?id=1435
+ http://groups.google.com/group/golang-nuts/browse_thread/thread/059597aafdd84a0e
+
+The following table summarizes the behaviours:
+ openbsd: safe
+ freebsd: safe
+ darwin: safe
+ linux: unsafe
+ windows: not supported
+
+*/
+package mitigation
+
+import (
+ "os"
+ "runtime"
+ "syscall"
+)
+
+// Checks if it is possible to activate the mitigation.
+func CanActivate() bool {
+ if runtime.GOOS == "windows" || runtime.GOARCH == "arm" {
+ return false
+ }
+
+ uid := syscall.Getuid()
+ return uid == 0
+}
+
+// Activates the mitigation measurements.
+func Activate(uid int, gid int, path string) {
+ if !CanActivate() {
+ panic("Cannot activate mitigation measurements!")
+ }
+
+ // chroot directory
+ err := syscall.Chroot(path)
+ if err != nil {
+ panic(err)
+ }
+
+ // change directory to new /
+ err = syscall.Chdir("/")
+ if err != nil {
+ panic(err)
+ }
+
+ // drop all other groups
+ err = syscall.Setgroups([]int{gid})
+ if err != nil {
+ panic(err)
+ }
+
+ // verify the empty group list
+ gids, err := syscall.Getgroups()
+ if err != nil {
+ panic("Could not read groups!")
+ }
+ if len(gids) > 1 {
+ panic("Could not drop groups!")
+ } else if len(gids) == 1 {
+ if gids[0] != gid {
+ panic("Could not drop foreign groups!")
+ }
+ }
+
+ // change group
+ err = syscall.Setgid(gid)
+ if err != nil {
+ panic(err)
+ }
+
+ // verify the group change
+ ngid := syscall.Getgid()
+ if ngid != gid {
+ panic("Could not change group id!")
+ }
+
+ // change user
+ err = syscall.Setuid(uid)
+ if err != nil {
+ panic(err)
+ }
+
+ // verify the user change
+ nuid := syscall.Getuid()
+ if nuid != uid {
+ panic("Could not change user id!")
+ }
+
+ // now drop all environment variables
+ os.Clearenv()
+}
diff --git a/vendor/github.com/sarnowski/mitigation/mitigation_test.go b/vendor/github.com/sarnowski/mitigation/mitigation_test.go
new file mode 100644
index 0000000..0fafeab
--- /dev/null
+++ b/vendor/github.com/sarnowski/mitigation/mitigation_test.go
@@ -0,0 +1,173 @@
+package mitigation
+
+import (
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "os/user"
+ "runtime"
+ "strconv"
+ "syscall"
+ "testing"
+)
+
+const (
+ TEST_UID = 1000
+ TEST_GID = 1000
+
+ TEST_ROUTINES_COUNT = 100
+)
+
+// The test will only work when running as root.
+func TestCanActivate(t *testing.T) {
+ if !CanActivate() {
+ t.Fatal("Tests must run as root!")
+ }
+}
+
+// The test will only work when running as root.
+func TestActivate(t *testing.T) {
+ // create temporary directory to test chrooting
+ tmp, err := ioutil.TempDir("", "mitigationtest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(tmp)
+ err = syscall.Chmod(tmp, 0755)
+ if err != nil {
+ t.Fatal("Could not change temporary directory permissions!")
+ }
+
+ // improve cpu usage to test for broken os implementations of setuid()
+ runtime.GOMAXPROCS(2)
+
+ // create some go routines as root to later test them
+ var sync chan bool = make(chan bool)
+ for i := 0; i < TEST_ROUTINES_COUNT; i++ {
+ go func() {
+ // no op
+ sync <- true
+ }()
+ }
+ for i := 0; i < TEST_ROUTINES_COUNT; i++ {
+ <-sync
+ }
+
+ // modify environment
+ err = os.Setenv("malicous_env", "bad string")
+ if err != nil {
+ t.Fatal("Cannot setup environment variables!")
+ }
+ if len(os.Environ()) == 0 {
+ t.Fatal("Environ() or Setenv() are broken!")
+ }
+
+ // do it!
+ Activate(TEST_UID, TEST_GID, tmp)
+
+ // verify uids
+ uid := syscall.Getuid()
+ if uid != TEST_UID {
+ t.Error("Failed to change UID")
+ }
+ euid := syscall.Geteuid()
+ if euid != TEST_UID {
+ t.Error("Failed to change EUID")
+ }
+
+ // verify gid
+ gid := syscall.Getgid()
+ if gid != TEST_GID {
+ t.Error("Failed to change GID")
+ }
+
+ // verify groups
+ gids, err := syscall.Getgroups()
+ if err != nil {
+ t.Fatal("Could not get group list")
+ }
+ if len(gids) > 1 {
+ t.Error("Not all groups are dropped!")
+ } else if len(gids) == 1 {
+ if gids[0] != TEST_GID {
+ t.Error("Not all foreign groups are dropped!")
+ }
+ }
+
+ // verify directory
+ dh, err := os.Open("/")
+ if err != nil {
+ t.Fatal("Cannot open my root directory", err)
+ }
+ files, err := dh.Readdir(-1)
+ if err != nil {
+ t.Fatal("Cannot read my root directory")
+ }
+ if len(files) > 0 {
+ t.Error("Root not changed to empty temporary directory!")
+ }
+
+ // verify environment
+ if len(os.Environ()) > 0 {
+ t.Error("Environment variables found!")
+ }
+
+ // test setuid() behaviour
+ var results chan int = make(chan int)
+
+ // start multiple goroutines, in a good OS, all all routines
+ // should be switched to the new user
+ for i := 0; i < TEST_ROUTINES_COUNT; i++ {
+ go func() {
+ results <- syscall.Getuid()
+ }()
+ }
+
+ // check the results
+ for i := 0; i < TEST_ROUTINES_COUNT; i++ {
+ uid := <-results
+ if uid != TEST_UID {
+ t.Error("false uid: ", uid, " (you are using an unsafe os, read the package documentation!)")
+ break
+ }
+ }
+}
+
+func ExampleActivate() {
+ // prepare application execution and allocate ressources with
+ // root privileges (e.g. open port 80 for an http server)
+ listener, _ := net.Listen("tcp", ":80")
+
+
+ // on OpenBSD, the "www" user is dedicated to serve the
+ // /var/www/htdocs directory in a chrooted way
+ wwwUser, _ := user.Lookup("www")
+ uid, _ := strconv.Atoi(wwwUser.Uid)
+ gid, _ := strconv.Atoi(wwwUser.Gid)
+
+ // now drop all privileges and chroot!
+ Activate(uid, gid, "/var/www/htdocs")
+
+
+ // The application is now chrooted and only runs with "www"
+ // privileges.
+ http.Serve(listener, http.FileServer(http.Dir("/")))
+}
+
+func ExampleCanActivate() {
+ workDir := "/var/www"
+
+ if CanActivate() {
+ // activate the mitigation and reset work directory to "/"
+ Activate(67, 67, workDir)
+ workDir = "/"
+ } else {
+ // we can handle this but log a warning
+ log.Println("WARNING: Cannot activate mitigation!")
+ }
+
+ // use "workDir" to address our files
+ log.Println("Working directory: ", workDir)
+}
diff --git a/vendor/golang.org/x/tools/.gitattributes b/vendor/golang.org/x/tools/.gitattributes
new file mode 100644
index 0000000..d2f212e
--- /dev/null
+++ b/vendor/golang.org/x/tools/.gitattributes
@@ -0,0 +1,10 @@
+# Treat all files in this repo as binary, with no git magic updating
+# line endings. Windows users contributing to Go will need to use a
+# modern version of git and editors capable of LF line endings.
+#
+# We'll prevent accidental CRLF line endings from entering the repo
+# via the git-review gofmt checks.
+#
+# See golang.org/issue/9281
+
+* -text
diff --git a/vendor/golang.org/x/tools/.gitignore b/vendor/golang.org/x/tools/.gitignore
new file mode 100644
index 0000000..5a9d62e
--- /dev/null
+++ b/vendor/golang.org/x/tools/.gitignore
@@ -0,0 +1,2 @@
+# Add no patterns to .gitignore except for files generated by the build.
+last-change
diff --git a/vendor/golang.org/x/tools/AUTHORS b/vendor/golang.org/x/tools/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/vendor/golang.org/x/tools/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/tools/CONTRIBUTING.md b/vendor/golang.org/x/tools/CONTRIBUTING.md
new file mode 100644
index 0000000..88dff59
--- /dev/null
+++ b/vendor/golang.org/x/tools/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing to Go
+
+Go is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+
+## Filing issues
+
+When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+**We do not accept GitHub pull requests**
+(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
+
diff --git a/vendor/golang.org/x/tools/CONTRIBUTORS b/vendor/golang.org/x/tools/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/vendor/golang.org/x/tools/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/tools/LICENSE b/vendor/golang.org/x/tools/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/golang.org/x/tools/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. 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.
+ * Neither the name of Google Inc. nor the names of its
+contributors may 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
+OWNER 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.
diff --git a/vendor/golang.org/x/tools/PATENTS b/vendor/golang.org/x/tools/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/vendor/golang.org/x/tools/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google 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,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/tools/README b/vendor/golang.org/x/tools/README
new file mode 100644
index 0000000..d5944c6
--- /dev/null
+++ b/vendor/golang.org/x/tools/README
@@ -0,0 +1,10 @@
+This subrepository holds the source for various packages and tools that support
+the Go programming language.
+
+Some of the tools, godoc and vet for example, are included in binary Go distributions.
+Others, including the Go guru and the test coverage tool, can be fetched with "go get".
+
+Packages include a type-checker for Go and an implementation of the
+Static Single Assignment form (SSA) representation for Go programs.
+
+To submit changes to this repository, see http://golang.org/doc/contribute.html.
diff --git a/vendor/golang.org/x/tools/blog/atom/atom.go b/vendor/golang.org/x/tools/blog/atom/atom.go
new file mode 100644
index 0000000..542c50e
--- /dev/null
+++ b/vendor/golang.org/x/tools/blog/atom/atom.go
@@ -0,0 +1,61 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Adapted from encoding/xml/read_test.go.
+
+// Package atom defines XML data structures for an Atom feed.
+package atom // import "golang.org/x/tools/blog/atom"
+
+import (
+ "encoding/xml"
+ "time"
+)
+
+type Feed struct {
+ XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"`
+ Title string `xml:"title"`
+ ID string `xml:"id"`
+ Link []Link `xml:"link"`
+ Updated TimeStr `xml:"updated"`
+ Author *Person `xml:"author"`
+ Entry []*Entry `xml:"entry"`
+}
+
+type Entry struct {
+ Title string `xml:"title"`
+ ID string `xml:"id"`
+ Link []Link `xml:"link"`
+ Published TimeStr `xml:"published"`
+ Updated TimeStr `xml:"updated"`
+ Author *Person `xml:"author"`
+ Summary *Text `xml:"summary"`
+ Content *Text `xml:"content"`
+}
+
+type Link struct {
+ Rel string `xml:"rel,attr,omitempty"`
+ Href string `xml:"href,attr"`
+ Type string `xml:"type,attr,omitempty"`
+ HrefLang string `xml:"hreflang,attr,omitempty"`
+ Title string `xml:"title,attr,omitempty"`
+ Length uint `xml:"length,attr,omitempty"`
+}
+
+type Person struct {
+ Name string `xml:"name"`
+ URI string `xml:"uri,omitempty"`
+ Email string `xml:"email,omitempty"`
+ InnerXML string `xml:",innerxml"`
+}
+
+type Text struct {
+ Type string `xml:"type,attr"`
+ Body string `xml:",chardata"`
+}
+
+type TimeStr string
+
+func Time(t time.Time) TimeStr {
+ return TimeStr(t.Format("2006-01-02T15:04:05-07:00"))
+}
diff --git a/vendor/golang.org/x/tools/blog/blog.go b/vendor/golang.org/x/tools/blog/blog.go
new file mode 100644
index 0000000..23c8dc6
--- /dev/null
+++ b/vendor/golang.org/x/tools/blog/blog.go
@@ -0,0 +1,424 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package blog implements a web server for articles written in present format.
+package blog // import "golang.org/x/tools/blog"
+
+import (
+ "bytes"
+ "encoding/json"
+ "encoding/xml"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+ "time"
+
+ "golang.org/x/tools/blog/atom"
+ "golang.org/x/tools/present"
+)
+
+var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)
+
+// Config specifies Server configuration values.
+type Config struct {
+ ContentPath string // Relative or absolute location of article files and related content.
+ TemplatePath string // Relative or absolute location of template files.
+
+ BaseURL string // Absolute base URL (for permalinks; no trailing slash).
+ BasePath string // Base URL path relative to server root (no trailing slash).
+ GodocURL string // The base URL of godoc (for menu bar; no trailing slash).
+ Hostname string // Server host name, used for rendering ATOM feeds.
+
+ HomeArticles int // Articles to display on the home page.
+ FeedArticles int // Articles to include in Atom and JSON feeds.
+ FeedTitle string // The title of the Atom XML feed
+
+ PlayEnabled bool
+}
+
+// Doc represents an article adorned with presentation data.
+type Doc struct {
+ *present.Doc
+ Permalink string // Canonical URL for this document.
+ Path string // Path relative to server root (including base).
+ HTML template.HTML // rendered article
+
+ Related []*Doc
+ Newer, Older *Doc
+}
+
+// Server implements an http.Handler that serves blog articles.
+type Server struct {
+ cfg Config
+ docs []*Doc
+ tags []string
+ docPaths map[string]*Doc // key is path without BasePath.
+ docTags map[string][]*Doc
+ template struct {
+ home, index, article, doc *template.Template
+ }
+ atomFeed []byte // pre-rendered Atom feed
+ jsonFeed []byte // pre-rendered JSON feed
+ content http.Handler
+}
+
+// NewServer constructs a new Server using the specified config.
+func NewServer(cfg Config) (*Server, error) {
+ present.PlayEnabled = cfg.PlayEnabled
+
+ root := filepath.Join(cfg.TemplatePath, "root.tmpl")
+ parse := func(name string) (*template.Template, error) {
+ t := template.New("").Funcs(funcMap)
+ return t.ParseFiles(root, filepath.Join(cfg.TemplatePath, name))
+ }
+
+ s := &Server{cfg: cfg}
+
+ // Parse templates.
+ var err error
+ s.template.home, err = parse("home.tmpl")
+ if err != nil {
+ return nil, err
+ }
+ s.template.index, err = parse("index.tmpl")
+ if err != nil {
+ return nil, err
+ }
+ s.template.article, err = parse("article.tmpl")
+ if err != nil {
+ return nil, err
+ }
+ p := present.Template().Funcs(funcMap)
+ s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl"))
+ if err != nil {
+ return nil, err
+ }
+
+ // Load content.
+ err = s.loadDocs(filepath.Clean(cfg.ContentPath))
+ if err != nil {
+ return nil, err
+ }
+
+ err = s.renderAtomFeed()
+ if err != nil {
+ return nil, err
+ }
+
+ err = s.renderJSONFeed()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set up content file server.
+ s.content = http.StripPrefix(s.cfg.BasePath, http.FileServer(http.Dir(cfg.ContentPath)))
+
+ return s, nil
+}
+
+var funcMap = template.FuncMap{
+ "sectioned": sectioned,
+ "authors": authors,
+}
+
+// sectioned returns true if the provided Doc contains more than one section.
+// This is used to control whether to display the table of contents and headings.
+func sectioned(d *present.Doc) bool {
+ return len(d.Sections) > 1
+}
+
+// authors returns a comma-separated list of author names.
+func authors(authors []present.Author) string {
+ var b bytes.Buffer
+ last := len(authors) - 1
+ for i, a := range authors {
+ if i > 0 {
+ if i == last {
+ b.WriteString(" and ")
+ } else {
+ b.WriteString(", ")
+ }
+ }
+ b.WriteString(authorName(a))
+ }
+ return b.String()
+}
+
+// authorName returns the first line of the Author text: the author's name.
+func authorName(a present.Author) string {
+ el := a.TextElem()
+ if len(el) == 0 {
+ return ""
+ }
+ text, ok := el[0].(present.Text)
+ if !ok || len(text.Lines) == 0 {
+ return ""
+ }
+ return text.Lines[0]
+}
+
+// loadDocs reads all content from the provided file system root, renders all
+// the articles it finds, adds them to the Server's docs field, computes the
+// denormalized docPaths, docTags, and tags fields, and populates the various
+// helper fields (Next, Previous, Related) for each Doc.
+func (s *Server) loadDocs(root string) error {
+ // Read content into docs field.
+ const ext = ".article"
+ fn := func(p string, info os.FileInfo, err error) error {
+ if filepath.Ext(p) != ext {
+ return nil
+ }
+ f, err := os.Open(p)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ d, err := present.Parse(f, p, 0)
+ if err != nil {
+ return err
+ }
+ html := new(bytes.Buffer)
+ err = d.Render(html, s.template.doc)
+ if err != nil {
+ return err
+ }
+ p = p[len(root) : len(p)-len(ext)] // trim root and extension
+ p = filepath.ToSlash(p)
+ s.docs = append(s.docs, &Doc{
+ Doc: d,
+ Path: s.cfg.BasePath + p,
+ Permalink: s.cfg.BaseURL + p,
+ HTML: template.HTML(html.String()),
+ })
+ return nil
+ }
+ err := filepath.Walk(root, fn)
+ if err != nil {
+ return err
+ }
+ sort.Sort(docsByTime(s.docs))
+
+ // Pull out doc paths and tags and put in reverse-associating maps.
+ s.docPaths = make(map[string]*Doc)
+ s.docTags = make(map[string][]*Doc)
+ for _, d := range s.docs {
+ s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d
+ for _, t := range d.Tags {
+ s.docTags[t] = append(s.docTags[t], d)
+ }
+ }
+
+ // Pull out unique sorted list of tags.
+ for t := range s.docTags {
+ s.tags = append(s.tags, t)
+ }
+ sort.Strings(s.tags)
+
+ // Set up presentation-related fields, Newer, Older, and Related.
+ for _, doc := range s.docs {
+ // Newer, Older: docs adjacent to doc
+ for i := range s.docs {
+ if s.docs[i] != doc {
+ continue
+ }
+ if i > 0 {
+ doc.Newer = s.docs[i-1]
+ }
+ if i+1 < len(s.docs) {
+ doc.Older = s.docs[i+1]
+ }
+ break
+ }
+
+ // Related: all docs that share tags with doc.
+ related := make(map[*Doc]bool)
+ for _, t := range doc.Tags {
+ for _, d := range s.docTags[t] {
+ if d != doc {
+ related[d] = true
+ }
+ }
+ }
+ for d := range related {
+ doc.Related = append(doc.Related, d)
+ }
+ sort.Sort(docsByTime(doc.Related))
+ }
+
+ return nil
+}
+
+// renderAtomFeed generates an XML Atom feed and stores it in the Server's
+// atomFeed field.
+func (s *Server) renderAtomFeed() error {
+ var updated time.Time
+ if len(s.docs) > 0 {
+ updated = s.docs[0].Time
+ }
+ feed := atom.Feed{
+ Title: s.cfg.FeedTitle,
+ ID: "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname,
+ Updated: atom.Time(updated),
+ Link: []atom.Link{{
+ Rel: "self",
+ Href: s.cfg.BaseURL + "/feed.atom",
+ }},
+ }
+ for i, doc := range s.docs {
+ if i >= s.cfg.FeedArticles {
+ break
+ }
+ e := &atom.Entry{
+ Title: doc.Title,
+ ID: feed.ID + doc.Path,
+ Link: []atom.Link{{
+ Rel: "alternate",
+ Href: doc.Permalink,
+ }},
+ Published: atom.Time(doc.Time),
+ Updated: atom.Time(doc.Time),
+ Summary: &atom.Text{
+ Type: "html",
+ Body: summary(doc),
+ },
+ Content: &atom.Text{
+ Type: "html",
+ Body: string(doc.HTML),
+ },
+ Author: &atom.Person{
+ Name: authors(doc.Authors),
+ },
+ }
+ feed.Entry = append(feed.Entry, e)
+ }
+ data, err := xml.Marshal(&feed)
+ if err != nil {
+ return err
+ }
+ s.atomFeed = data
+ return nil
+}
+
+type jsonItem struct {
+ Title string
+ Link string
+ Time time.Time
+ Summary string
+ Content string
+ Author string
+}
+
+// renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed
+// field.
+func (s *Server) renderJSONFeed() error {
+ var feed []jsonItem
+ for i, doc := range s.docs {
+ if i >= s.cfg.FeedArticles {
+ break
+ }
+ item := jsonItem{
+ Title: doc.Title,
+ Link: doc.Permalink,
+ Time: doc.Time,
+ Summary: summary(doc),
+ Content: string(doc.HTML),
+ Author: authors(doc.Authors),
+ }
+ feed = append(feed, item)
+ }
+ data, err := json.Marshal(feed)
+ if err != nil {
+ return err
+ }
+ s.jsonFeed = data
+ return nil
+}
+
+// summary returns the first paragraph of text from the provided Doc.
+func summary(d *Doc) string {
+ if len(d.Sections) == 0 {
+ return ""
+ }
+ for _, elem := range d.Sections[0].Elem {
+ text, ok := elem.(present.Text)
+ if !ok || text.Pre {
+ // skip everything but non-text elements
+ continue
+ }
+ var buf bytes.Buffer
+ for _, s := range text.Lines {
+ buf.WriteString(string(present.Style(s)))
+ buf.WriteByte('\n')
+ }
+ return buf.String()
+ }
+ return ""
+}
+
+// rootData encapsulates data destined for the root template.
+type rootData struct {
+ Doc *Doc
+ BasePath string
+ GodocURL string
+ Data interface{}
+}
+
+// ServeHTTP serves the front, index, and article pages
+// as well as the ATOM and JSON feeds.
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ var (
+ d = rootData{BasePath: s.cfg.BasePath, GodocURL: s.cfg.GodocURL}
+ t *template.Template
+ )
+ switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p {
+ case "/":
+ d.Data = s.docs
+ if len(s.docs) > s.cfg.HomeArticles {
+ d.Data = s.docs[:s.cfg.HomeArticles]
+ }
+ t = s.template.home
+ case "/index":
+ d.Data = s.docs
+ t = s.template.index
+ case "/feed.atom", "/feeds/posts/default":
+ w.Header().Set("Content-type", "application/atom+xml; charset=utf-8")
+ w.Write(s.atomFeed)
+ return
+ case "/.json":
+ if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) {
+ w.Header().Set("Content-type", "application/javascript; charset=utf-8")
+ fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed)
+ return
+ }
+ w.Header().Set("Content-type", "application/json; charset=utf-8")
+ w.Write(s.jsonFeed)
+ return
+ default:
+ doc, ok := s.docPaths[p]
+ if !ok {
+ // Not a doc; try to just serve static content.
+ s.content.ServeHTTP(w, r)
+ return
+ }
+ d.Doc = doc
+ t = s.template.article
+ }
+ err := t.ExecuteTemplate(w, "root", d)
+ if err != nil {
+ log.Println(err)
+ }
+}
+
+// docsByTime implements sort.Interface, sorting Docs by their Time field.
+type docsByTime []*Doc
+
+func (s docsByTime) Len() int { return len(s) }
+func (s docsByTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
diff --git a/vendor/golang.org/x/tools/codereview.cfg b/vendor/golang.org/x/tools/codereview.cfg
new file mode 100644
index 0000000..3f8b14b
--- /dev/null
+++ b/vendor/golang.org/x/tools/codereview.cfg
@@ -0,0 +1 @@
+issuerepo: golang/go
diff --git a/vendor/golang.org/x/tools/present/args.go b/vendor/golang.org/x/tools/present/args.go
new file mode 100644
index 0000000..49ee1a9
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/args.go
@@ -0,0 +1,229 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "errors"
+ "regexp"
+ "strconv"
+ "unicode/utf8"
+)
+
+// This file is stolen from go/src/cmd/godoc/codewalk.go.
+// It's an evaluator for the file address syntax implemented by acme and sam,
+// but using Go-native regular expressions.
+// To keep things reasonably close, this version uses (?m:re) for all user-provided
+// regular expressions. That is the only change to the code from codewalk.go.
+// See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
+// for details on the syntax.
+
+// addrToByte evaluates the given address starting at offset start in data.
+// It returns the lo and hi byte offset of the matched region within data.
+func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) {
+ if addr == "" {
+ lo, hi = start, len(data)
+ return
+ }
+ var (
+ dir byte
+ prevc byte
+ charOffset bool
+ )
+ lo = start
+ hi = start
+ for addr != "" && err == nil {
+ c := addr[0]
+ switch c {
+ default:
+ err = errors.New("invalid address syntax near " + string(c))
+ case ',':
+ if len(addr) == 1 {
+ hi = len(data)
+ } else {
+ _, hi, err = addrToByteRange(addr[1:], hi, data)
+ }
+ return
+
+ case '+', '-':
+ if prevc == '+' || prevc == '-' {
+ lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset)
+ }
+ dir = c
+
+ case '$':
+ lo = len(data)
+ hi = len(data)
+ if len(addr) > 1 {
+ dir = '+'
+ }
+
+ case '#':
+ charOffset = true
+
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ var i int
+ for i = 1; i < len(addr); i++ {
+ if addr[i] < '0' || addr[i] > '9' {
+ break
+ }
+ }
+ var n int
+ n, err = strconv.Atoi(addr[0:i])
+ if err != nil {
+ break
+ }
+ lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset)
+ dir = 0
+ charOffset = false
+ prevc = c
+ addr = addr[i:]
+ continue
+
+ case '/':
+ var i, j int
+ Regexp:
+ for i = 1; i < len(addr); i++ {
+ switch addr[i] {
+ case '\\':
+ i++
+ case '/':
+ j = i + 1
+ break Regexp
+ }
+ }
+ if j == 0 {
+ j = i
+ }
+ pattern := addr[1:i]
+ lo, hi, err = addrRegexp(data, lo, hi, dir, pattern)
+ prevc = c
+ addr = addr[j:]
+ continue
+ }
+ prevc = c
+ addr = addr[1:]
+ }
+
+ if err == nil && dir != 0 {
+ lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset)
+ }
+ if err != nil {
+ return 0, 0, err
+ }
+ return lo, hi, nil
+}
+
+// addrNumber applies the given dir, n, and charOffset to the address lo, hi.
+// dir is '+' or '-', n is the count, and charOffset is true if the syntax
+// used was #n. Applying +n (or +#n) means to advance n lines
+// (or characters) after hi. Applying -n (or -#n) means to back up n lines
+// (or characters) before lo.
+// The return value is the new lo, hi.
+func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) {
+ switch dir {
+ case 0:
+ lo = 0
+ hi = 0
+ fallthrough
+
+ case '+':
+ if charOffset {
+ pos := hi
+ for ; n > 0 && pos < len(data); n-- {
+ _, size := utf8.DecodeRune(data[pos:])
+ pos += size
+ }
+ if n == 0 {
+ return pos, pos, nil
+ }
+ break
+ }
+ // find next beginning of line
+ if hi > 0 {
+ for hi < len(data) && data[hi-1] != '\n' {
+ hi++
+ }
+ }
+ lo = hi
+ if n == 0 {
+ return lo, hi, nil
+ }
+ for ; hi < len(data); hi++ {
+ if data[hi] != '\n' {
+ continue
+ }
+ switch n--; n {
+ case 1:
+ lo = hi + 1
+ case 0:
+ return lo, hi + 1, nil
+ }
+ }
+
+ case '-':
+ if charOffset {
+ // Scan backward for bytes that are not UTF-8 continuation bytes.
+ pos := lo
+ for ; pos > 0 && n > 0; pos-- {
+ if data[pos]&0xc0 != 0x80 {
+ n--
+ }
+ }
+ if n == 0 {
+ return pos, pos, nil
+ }
+ break
+ }
+ // find earlier beginning of line
+ for lo > 0 && data[lo-1] != '\n' {
+ lo--
+ }
+ hi = lo
+ if n == 0 {
+ return lo, hi, nil
+ }
+ for ; lo >= 0; lo-- {
+ if lo > 0 && data[lo-1] != '\n' {
+ continue
+ }
+ switch n--; n {
+ case 1:
+ hi = lo
+ case 0:
+ return lo, hi, nil
+ }
+ }
+ }
+
+ return 0, 0, errors.New("address out of range")
+}
+
+// addrRegexp searches for pattern in the given direction starting at lo, hi.
+// The direction dir is '+' (search forward from hi) or '-' (search backward from lo).
+// Backward searches are unimplemented.
+func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) {
+ // We want ^ and $ to work as in sam/acme, so use ?m.
+ re, err := regexp.Compile("(?m:" + pattern + ")")
+ if err != nil {
+ return 0, 0, err
+ }
+ if dir == '-' {
+ // Could implement reverse search using binary search
+ // through file, but that seems like overkill.
+ return 0, 0, errors.New("reverse search not implemented")
+ }
+ m := re.FindIndex(data[hi:])
+ if len(m) > 0 {
+ m[0] += hi
+ m[1] += hi
+ } else if hi > 0 {
+ // No match. Wrap to beginning of data.
+ m = re.FindIndex(data)
+ }
+ if len(m) == 0 {
+ return 0, 0, errors.New("no match for " + pattern)
+ }
+ return m[0], m[1], nil
+}
diff --git a/vendor/golang.org/x/tools/present/caption.go b/vendor/golang.org/x/tools/present/caption.go
new file mode 100644
index 0000000..00e0b5d
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/caption.go
@@ -0,0 +1,22 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import "strings"
+
+func init() {
+ Register("caption", parseCaption)
+}
+
+type Caption struct {
+ Text string
+}
+
+func (c Caption) TemplateName() string { return "caption" }
+
+func parseCaption(_ *Context, _ string, _ int, text string) (Elem, error) {
+ text = strings.TrimSpace(strings.TrimPrefix(text, ".caption"))
+ return Caption{text}, nil
+}
diff --git a/vendor/golang.org/x/tools/present/code.go b/vendor/golang.org/x/tools/present/code.go
new file mode 100644
index 0000000..b47a72a
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/code.go
@@ -0,0 +1,267 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "html/template"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// PlayEnabled specifies whether runnable playground snippets should be
+// displayed in the present user interface.
+var PlayEnabled = false
+
+// TODO(adg): replace the PlayEnabled flag with something less spaghetti-like.
+// Instead this will probably be determined by a template execution Context
+// value that contains various global metadata required when rendering
+// templates.
+
+// NotesEnabled specifies whether presenter notes should be displayed in the
+// present user interface.
+var NotesEnabled = false
+
+func init() {
+ Register("code", parseCode)
+ Register("play", parseCode)
+}
+
+type Code struct {
+ Text template.HTML
+ Play bool // runnable code
+ Edit bool // editable code
+ FileName string // file name
+ Ext string // file extension
+ Raw []byte // content of the file
+}
+
+func (c Code) TemplateName() string { return "code" }
+
+// The input line is a .code or .play entry with a file name and an optional HLfoo marker on the end.
+// Anything between the file and HL (if any) is an address expression, which we treat as a string here.
+// We pick off the HL first, for easy parsing.
+var (
+ highlightRE = regexp.MustCompile(`\s+HL([a-zA-Z0-9_]+)?$`)
+ hlCommentRE = regexp.MustCompile(`(.+) // HL(.*)$`)
+ codeRE = regexp.MustCompile(`\.(code|play)\s+((?:(?:-edit|-numbers)\s+)*)([^\s]+)(?:\s+(.*))?$`)
+)
+
+// parseCode parses a code present directive. Its syntax:
+// .code [-numbers] [-edit] <filename> [address] [highlight]
+// The directive may also be ".play" if the snippet is executable.
+func parseCode(ctx *Context, sourceFile string, sourceLine int, cmd string) (Elem, error) {
+ cmd = strings.TrimSpace(cmd)
+
+ // Pull off the HL, if any, from the end of the input line.
+ highlight := ""
+ if hl := highlightRE.FindStringSubmatchIndex(cmd); len(hl) == 4 {
+ if hl[2] < 0 || hl[3] < 0 {
+ return nil, fmt.Errorf("%s:%d invalid highlight syntax", sourceFile, sourceLine)
+ }
+ highlight = cmd[hl[2]:hl[3]]
+ cmd = cmd[:hl[2]-2]
+ }
+
+ // Parse the remaining command line.
+ // Arguments:
+ // args[0]: whole match
+ // args[1]: .code/.play
+ // args[2]: flags ("-edit -numbers")
+ // args[3]: file name
+ // args[4]: optional address
+ args := codeRE.FindStringSubmatch(cmd)
+ if len(args) != 5 {
+ return nil, fmt.Errorf("%s:%d: syntax error for .code/.play invocation", sourceFile, sourceLine)
+ }
+ command, flags, file, addr := args[1], args[2], args[3], strings.TrimSpace(args[4])
+ play := command == "play" && PlayEnabled
+
+ // Read in code file and (optionally) match address.
+ filename := filepath.Join(filepath.Dir(sourceFile), file)
+ textBytes, err := ctx.ReadFile(filename)
+ if err != nil {
+ return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
+ }
+ lo, hi, err := addrToByteRange(addr, 0, textBytes)
+ if err != nil {
+ return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
+ }
+ if lo > hi {
+ // The search in addrToByteRange can wrap around so we might
+ // end up with the range ending before its starting point
+ hi, lo = lo, hi
+ }
+
+ // Acme pattern matches can stop mid-line,
+ // so run to end of line in both directions if not at line start/end.
+ for lo > 0 && textBytes[lo-1] != '\n' {
+ lo--
+ }
+ if hi > 0 {
+ for hi < len(textBytes) && textBytes[hi-1] != '\n' {
+ hi++
+ }
+ }
+
+ lines := codeLines(textBytes, lo, hi)
+
+ data := &codeTemplateData{
+ Lines: formatLines(lines, highlight),
+ Edit: strings.Contains(flags, "-edit"),
+ Numbers: strings.Contains(flags, "-numbers"),
+ }
+
+ // Include before and after in a hidden span for playground code.
+ if play {
+ data.Prefix = textBytes[:lo]
+ data.Suffix = textBytes[hi:]
+ }
+
+ var buf bytes.Buffer
+ if err := codeTemplate.Execute(&buf, data); err != nil {
+ return nil, err
+ }
+ return Code{
+ Text: template.HTML(buf.String()),
+ Play: play,
+ Edit: data.Edit,
+ FileName: filepath.Base(filename),
+ Ext: filepath.Ext(filename),
+ Raw: rawCode(lines),
+ }, nil
+}
+
+// formatLines returns a new slice of codeLine with the given lines
+// replacing tabs with spaces and adding highlighting where needed.
+func formatLines(lines []codeLine, highlight string) []codeLine {
+ formatted := make([]codeLine, len(lines))
+ for i, line := range lines {
+ // Replace tabs with spaces, which work better in HTML.
+ line.L = strings.Replace(line.L, "\t", " ", -1)
+
+ // Highlight lines that end with "// HL[highlight]"
+ // and strip the magic comment.
+ if m := hlCommentRE.FindStringSubmatch(line.L); m != nil {
+ line.L = m[1]
+ line.HL = m[2] == highlight
+ }
+
+ formatted[i] = line
+ }
+ return formatted
+}
+
+// rawCode returns the code represented by the given codeLines without any kind
+// of formatting.
+func rawCode(lines []codeLine) []byte {
+ b := new(bytes.Buffer)
+ for _, line := range lines {
+ b.WriteString(line.L)
+ b.WriteByte('\n')
+ }
+ return b.Bytes()
+}
+
+type codeTemplateData struct {
+ Lines []codeLine
+ Prefix, Suffix []byte
+ Edit, Numbers bool
+}
+
+var leadingSpaceRE = regexp.MustCompile(`^[ \t]*`)
+
+var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{
+ "trimSpace": strings.TrimSpace,
+ "leadingSpace": leadingSpaceRE.FindString,
+}).Parse(codeTemplateHTML))
+
+const codeTemplateHTML = `
+{{with .Prefix}}<pre style="display: none"><span>{{printf "%s" .}}</span></pre>{{end}}
+
+<pre{{if .Edit}} contenteditable="true" spellcheck="false"{{end}}{{if .Numbers}} class="numbers"{{end}}>{{/*
+ */}}{{range .Lines}}<span num="{{.N}}">{{/*
+ */}}{{if .HL}}{{leadingSpace .L}}<b>{{trimSpace .L}}</b>{{/*
+ */}}{{else}}{{.L}}{{end}}{{/*
+*/}}</span>
+{{end}}</pre>
+
+{{with .Suffix}}<pre style="display: none"><span>{{printf "%s" .}}</span></pre>{{end}}
+`
+
+// codeLine represents a line of code extracted from a source file.
+type codeLine struct {
+ L string // The line of code.
+ N int // The line number from the source file.
+ HL bool // Whether the line should be highlighted.
+}
+
+// codeLines takes a source file and returns the lines that
+// span the byte range specified by start and end.
+// It discards lines that end in "OMIT".
+func codeLines(src []byte, start, end int) (lines []codeLine) {
+ startLine := 1
+ for i, b := range src {
+ if i == start {
+ break
+ }
+ if b == '\n' {
+ startLine++
+ }
+ }
+ s := bufio.NewScanner(bytes.NewReader(src[start:end]))
+ for n := startLine; s.Scan(); n++ {
+ l := s.Text()
+ if strings.HasSuffix(l, "OMIT") {
+ continue
+ }
+ lines = append(lines, codeLine{L: l, N: n})
+ }
+ // Trim leading and trailing blank lines.
+ for len(lines) > 0 && len(lines[0].L) == 0 {
+ lines = lines[1:]
+ }
+ for len(lines) > 0 && len(lines[len(lines)-1].L) == 0 {
+ lines = lines[:len(lines)-1]
+ }
+ return
+}
+
+func parseArgs(name string, line int, args []string) (res []interface{}, err error) {
+ res = make([]interface{}, len(args))
+ for i, v := range args {
+ if len(v) == 0 {
+ return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
+ }
+ switch v[0] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ n, err := strconv.Atoi(v)
+ if err != nil {
+ return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
+ }
+ res[i] = n
+ case '/':
+ if len(v) < 2 || v[len(v)-1] != '/' {
+ return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
+ }
+ res[i] = v
+ case '$':
+ res[i] = "$"
+ case '_':
+ if len(v) == 1 {
+ // Do nothing; "_" indicates an intentionally empty parameter.
+ break
+ }
+ fallthrough
+ default:
+ return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
+ }
+ }
+ return
+}
diff --git a/vendor/golang.org/x/tools/present/code_test.go b/vendor/golang.org/x/tools/present/code_test.go
new file mode 100644
index 0000000..b01a4e3
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/code_test.go
@@ -0,0 +1,225 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "html/template"
+ "strings"
+ "testing"
+)
+
+func TestParseCode(t *testing.T) {
+ // Enable play but revert the change at the end.
+ defer func(play bool) { PlayEnabled = play }(PlayEnabled)
+ PlayEnabled = true
+
+ helloTest := []byte(`
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("hello, test")
+}
+`)
+ helloTestHTML := template.HTML(`
+<pre><span num="2">package main</span>
+<span num="3"></span>
+<span num="4">import &#34;fmt&#34;</span>
+<span num="5"></span>
+<span num="6">func main() {</span>
+<span num="7"> fmt.Println(&#34;hello, test&#34;)</span>
+<span num="8">}</span>
+</pre>
+`)
+ helloTestHL := []byte(`
+package main
+
+import "fmt" // HLimport
+
+func main() { // HLfunc
+ fmt.Println("hello, test") // HL
+}
+`)
+ highlight := func(h template.HTML, s string) template.HTML {
+ return template.HTML(strings.Replace(string(h), s, "<b>"+s+"</b>", -1))
+ }
+ read := func(b []byte, err error) func(string) ([]byte, error) {
+ return func(string) ([]byte, error) { return b, err }
+ }
+
+ tests := []struct {
+ name string
+ readFile func(string) ([]byte, error)
+ sourceFile string
+ cmd string
+ err string
+ Code
+ }{
+ {
+ name: "all code, no play",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Raw: helloTest,
+ Text: helloTestHTML,
+ },
+ },
+ {
+ name: "all code, play",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".play main.go",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Play: true,
+ Raw: helloTest,
+ Text: helloTestHTML,
+ },
+ },
+ {
+ name: "all code, highlighted",
+ readFile: read(helloTestHL, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Raw: helloTestHL,
+ Text: highlight(helloTestHTML, "fmt.Println(&#34;hello, test&#34;)"),
+ },
+ },
+ {
+ name: "highlight only func",
+ readFile: read(helloTestHL, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go HLfunc",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Play: false,
+ Raw: []byte("package main\n\nimport \"fmt\" // HLimport\n\nfunc main() { // HLfunc\n\tfmt.Println(\"hello, test\") // HL\n}"),
+ Text: highlight(helloTestHTML, "func main() {"),
+ },
+ },
+ {
+ name: "bad highlight syntax",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go HL",
+ err: "invalid highlight syntax",
+ },
+ {
+ name: "error reading file",
+ readFile: read(nil, fmt.Errorf("nope")),
+ sourceFile: "main.go",
+ cmd: ".code main.go",
+ err: "main.go:0: nope",
+ },
+ {
+ name: "from func main to the end",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go /func main/,",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Play: false,
+ Raw: []byte("func main() {\n\tfmt.Println(\"hello, test\")\n}"),
+ Text: "<pre><span num=\"6\">func main() {</span>\n<span num=\"7\"> fmt.Println(&#34;hello, test&#34;)</span>\n<span num=\"8\">}</span>\n</pre>",
+ },
+ },
+ {
+ name: "just func main",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go /func main/",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Play: false,
+ Raw: []byte("func main() {"),
+ Text: "<pre><span num=\"6\">func main() {</span>\n</pre>",
+ },
+ },
+ {
+ name: "bad address",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code main.go /function main/",
+ err: "main.go:0: no match for function main",
+ },
+ {
+ name: "all code with numbers",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code -numbers main.go",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Raw: helloTest,
+ // Replacing the first "<pre>"
+ Text: "<pre class=\"numbers\">" + helloTestHTML[6:],
+ },
+ },
+ {
+ name: "all code editable",
+ readFile: read(helloTest, nil),
+ sourceFile: "main.go",
+ cmd: ".code -edit main.go",
+ Code: Code{
+ Ext: ".go",
+ FileName: "main.go",
+ Raw: helloTest,
+ Text: "<pre contenteditable=\"true\" spellcheck=\"false\">" + helloTestHTML[6:],
+ },
+ },
+ }
+
+ trimHTML := func(t template.HTML) string { return strings.TrimSpace(string(t)) }
+ trimBytes := func(b []byte) string { return strings.TrimSpace(string(b)) }
+
+ for _, tt := range tests {
+ ctx := &Context{tt.readFile}
+ e, err := parseCode(ctx, tt.sourceFile, 0, tt.cmd)
+ if err != nil {
+ if tt.err == "" {
+ t.Errorf("%s: unexpected error %v", tt.name, err)
+ } else if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("%s: expected error %s; got %v", tt.name, tt.err, err)
+ }
+ continue
+ }
+ if tt.err != "" {
+ t.Errorf("%s: expected error %s; but got none", tt.name, tt.err)
+ continue
+ }
+ c, ok := e.(Code)
+ if !ok {
+ t.Errorf("%s: expected a Code value; got %T", tt.name, e)
+ continue
+ }
+ if c.FileName != tt.FileName {
+ t.Errorf("%s: expected FileName %s; got %s", tt.name, tt.FileName, c.FileName)
+ }
+ if c.Ext != tt.Ext {
+ t.Errorf("%s: expected Ext %s; got %s", tt.name, tt.Ext, c.Ext)
+ }
+ if c.Play != tt.Play {
+ t.Errorf("%s: expected Play %v; got %v", tt.name, tt.Play, c.Play)
+ }
+ if got, wants := trimBytes(c.Raw), trimBytes(tt.Raw); got != wants {
+ t.Errorf("%s: expected Raw \n%q\n; got \n%q\n", tt.name, wants, got)
+ }
+ if got, wants := trimHTML(c.Text), trimHTML(tt.Text); got != wants {
+ t.Errorf("%s: expected Text \n%q\n; got \n%q\n", tt.name, wants, got)
+ }
+ }
+}
diff --git a/vendor/golang.org/x/tools/present/doc.go b/vendor/golang.org/x/tools/present/doc.go
new file mode 100644
index 0000000..804ec84
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/doc.go
@@ -0,0 +1,266 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+The present file format
+
+Present files have the following format. The first non-blank non-comment
+line is the title, so the header looks like
+
+ Title of document
+ Subtitle of document
+ 15:04 2 Jan 2006
+ Tags: foo, bar, baz
+ <blank line>
+ Author Name
+ Job title, Company
+ joe@example.com
+ http://url/
+ @twitter_name
+
+The subtitle, date, and tags lines are optional.
+
+The date line may be written without a time:
+ 2 Jan 2006
+In this case, the time will be interpreted as 10am UTC on that date.
+
+The tags line is a comma-separated list of tags that may be used to categorize
+the document.
+
+The author section may contain a mixture of text, twitter names, and links.
+For slide presentations, only the plain text lines will be displayed on the
+first slide.
+
+Multiple presenters may be specified, separated by a blank line.
+
+After that come slides/sections, each after a blank line:
+
+ * Title of slide or section (must have asterisk)
+
+ Some Text
+
+ ** Subsection
+
+ - bullets
+ - more bullets
+ - a bullet with
+
+ *** Sub-subsection
+
+ Some More text
+
+ Preformatted text
+ is indented (however you like)
+
+ Further Text, including invocations like:
+
+ .code x.go /^func main/,/^}/
+ .play y.go
+ .image image.jpg
+ .background image.jpg
+ .iframe http://foo
+ .link http://foo label
+ .html file.html
+ .caption _Gopher_ by [[http://www.reneefrench.com][Renée French]]
+
+ Again, more text
+
+Blank lines are OK (not mandatory) after the title and after the
+text. Text, bullets, and .code etc. are all optional; title is
+not.
+
+Lines starting with # in column 1 are commentary.
+
+Fonts:
+
+Within the input for plain text or lists, text bracketed by font
+markers will be presented in italic, bold, or program font.
+Marker characters are _ (italic), * (bold) and ` (program font).
+An opening marker must be preceded by a space or punctuation
+character or else be at start of a line; similarly, a closing
+marker must be followed by a space or punctuation character or
+else be at the end of a line. Unmatched markers appear as plain text.
+There must be no spaces between markers. Within marked text,
+a single marker character becomes a space and a doubled single
+marker quotes the marker character.
+
+at the beginning of a line or
+else be preceded by a space or punctuation; similarly a closing
+marker must be at the end of the lineo
+
+ _italic_
+ *bold*
+ `program`
+ Markup—_especially_italic_text_—can easily be overused.
+ _Why_use_scoped__ptr_? Use plain ***ptr* instead.
+
+Inline links:
+
+Links can be included in any text with the form [[url][label]], or
+[[url]] to use the URL itself as the label.
+
+Functions:
+
+A number of template functions are available through invocations
+in the input text. Each such invocation contains a period as the
+first character on the line, followed immediately by the name of
+the function, followed by any arguments. A typical invocation might
+be
+ .play demo.go /^func show/,/^}/
+(except that the ".play" must be at the beginning of the line and
+not be indented like this.)
+
+Here follows a description of the functions:
+
+code:
+
+Injects program source into the output by extracting code from files
+and injecting them as HTML-escaped <pre> blocks. The argument is
+a file name followed by an optional address that specifies what
+section of the file to display. The address syntax is similar in
+its simplest form to that of ed, but comes from sam and is more
+general. See
+ http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
+for full details. The displayed block is always rounded out to a
+full line at both ends.
+
+If no pattern is present, the entire file is displayed.
+
+Any line in the program that ends with the four characters
+ OMIT
+is deleted from the source before inclusion, making it easy
+to write things like
+ .code test.go /START OMIT/,/END OMIT/
+to find snippets like this
+ tedious_code = boring_function()
+ // START OMIT
+ interesting_code = fascinating_function()
+ // END OMIT
+and see only this:
+ interesting_code = fascinating_function()
+
+Also, inside the displayed text a line that ends
+ // HL
+will be highlighted in the display; the 'h' key in the browser will
+toggle extra emphasis of any highlighted lines. A highlighting mark
+may have a suffix word, such as
+ // HLxxx
+Such highlights are enabled only if the code invocation ends with
+"HL" followed by the word:
+ .code test.go /^type Foo/,/^}/ HLxxx
+
+The .code function may take one or more flags immediately preceding
+the filename. This command shows test.go in an editable text area:
+ .code -edit test.go
+This command shows test.go with line numbers:
+ .code -numbers test.go
+
+play:
+
+The function "play" is the same as "code" but puts a button
+on the displayed source so the program can be run from the browser.
+Although only the selected text is shown, all the source is included
+in the HTML output so it can be presented to the compiler.
+
+link:
+
+Create a hyperlink. The syntax is 1 or 2 space-separated arguments.
+The first argument is always the HTTP URL. If there is a second
+argument, it is the text label to display for this link.
+
+ .link http://golang.org golang.org
+
+image:
+
+The template uses the function "image" to inject picture files.
+
+The syntax is simple: 1 or 3 space-separated arguments.
+The first argument is always the file name.
+If there are more arguments, they are the height and width;
+both must be present, or substituted with an underscore.
+Replacing a dimension argument with the underscore parameter
+preserves the aspect ratio of the image when scaling.
+
+ .image images/betsy.jpg 100 200
+
+ .image images/janet.jpg _ 300
+
+video:
+
+The template uses the function "video" to inject video files.
+
+The syntax is simple: 2 or 4 space-separated arguments.
+The first argument is always the file name.
+The second argument is always the file content-type.
+If there are more arguments, they are the height and width;
+both must be present, or substituted with an underscore.
+Replacing a dimension argument with the underscore parameter
+preserves the aspect ratio of the video when scaling.
+
+ .video videos/evangeline.mp4 video/mp4 400 600
+
+ .video videos/mabel.ogg video/ogg 500 _
+
+background:
+
+The template uses the function "background" to set the background image for
+a slide. The only argument is the file name of the image.
+
+ .background images/susan.jpg
+
+caption:
+
+The template uses the function "caption" to inject figure captions.
+
+The text after ".caption" is embedded in a figcaption element after
+processing styling and links as in standard text lines.
+
+ .caption _Gopher_ by [[http://www.reneefrench.com][Renée French]]
+
+iframe:
+
+The function "iframe" injects iframes (pages inside pages).
+Its syntax is the same as that of image.
+
+html:
+
+The function html includes the contents of the specified file as
+unescaped HTML. This is useful for including custom HTML elements
+that cannot be created using only the slide format.
+It is your responsibilty to make sure the included HTML is valid and safe.
+
+ .html file.html
+
+Presenter notes:
+
+Presenter notes may be enabled by appending the "-notes" flag when you run
+your "present" binary.
+
+This will allow you to open a second window by pressing 'N' from your browser
+displaying your slides. The second window is completely synced with your main
+window, except that presenter notes are only visible on the second window.
+
+Lines that begin with ": " are treated as presenter notes.
+
+ * Title of slide
+
+ Some Text
+
+ : Presenter notes (first paragraph)
+ : Presenter notes (subsequent paragraph(s))
+
+Notes may appear anywhere within the slide text. For example:
+
+ * Title of slide
+
+ : Presenter notes (first paragraph)
+
+ Some Text
+
+ : Presenter notes (subsequent paragraph(s))
+
+This has the same result as the example above.
+
+*/
+package present // import "golang.org/x/tools/present"
diff --git a/vendor/golang.org/x/tools/present/html.go b/vendor/golang.org/x/tools/present/html.go
new file mode 100644
index 0000000..cca90ef
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/html.go
@@ -0,0 +1,31 @@
+package present
+
+import (
+ "errors"
+ "html/template"
+ "path/filepath"
+ "strings"
+)
+
+func init() {
+ Register("html", parseHTML)
+}
+
+func parseHTML(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ p := strings.Fields(text)
+ if len(p) != 2 {
+ return nil, errors.New("invalid .html args")
+ }
+ name := filepath.Join(filepath.Dir(fileName), p[1])
+ b, err := ctx.ReadFile(name)
+ if err != nil {
+ return nil, err
+ }
+ return HTML{template.HTML(b)}, nil
+}
+
+type HTML struct {
+ template.HTML
+}
+
+func (s HTML) TemplateName() string { return "html" }
diff --git a/vendor/golang.org/x/tools/present/iframe.go b/vendor/golang.org/x/tools/present/iframe.go
new file mode 100644
index 0000000..46649f0
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/iframe.go
@@ -0,0 +1,45 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "strings"
+)
+
+func init() {
+ Register("iframe", parseIframe)
+}
+
+type Iframe struct {
+ URL string
+ Width int
+ Height int
+}
+
+func (i Iframe) TemplateName() string { return "iframe" }
+
+func parseIframe(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ i := Iframe{URL: args[1]}
+ a, err := parseArgs(fileName, lineno, args[2:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ if v, ok := a[0].(int); ok {
+ i.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ i.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect iframe invocation: %q", text)
+ }
+ return i, nil
+}
diff --git a/vendor/golang.org/x/tools/present/image.go b/vendor/golang.org/x/tools/present/image.go
new file mode 100644
index 0000000..cfa2af9
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/image.go
@@ -0,0 +1,50 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "strings"
+)
+
+func init() {
+ Register("image", parseImage)
+}
+
+type Image struct {
+ URL string
+ Width int
+ Height int
+}
+
+func (i Image) TemplateName() string { return "image" }
+
+func parseImage(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ img := Image{URL: args[1]}
+ a, err := parseArgs(fileName, lineno, args[2:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ // If a parameter is empty (underscore) or invalid
+ // leave the field set to zero. The "image" action
+ // template will then omit that img tag attribute and
+ // the browser will calculate the value to preserve
+ // the aspect ratio.
+ if v, ok := a[0].(int); ok {
+ img.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ img.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect image invocation: %q", text)
+ }
+ return img, nil
+}
diff --git a/vendor/golang.org/x/tools/present/link.go b/vendor/golang.org/x/tools/present/link.go
new file mode 100644
index 0000000..2aead35
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/link.go
@@ -0,0 +1,100 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "log"
+ "net/url"
+ "strings"
+)
+
+func init() {
+ Register("link", parseLink)
+}
+
+type Link struct {
+ URL *url.URL
+ Label string
+}
+
+func (l Link) TemplateName() string { return "link" }
+
+func parseLink(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ if len(args) < 2 {
+ return nil, fmt.Errorf("link element must have at least 2 arguments")
+ }
+ url, err := url.Parse(args[1])
+ if err != nil {
+ return nil, err
+ }
+ label := ""
+ if len(args) > 2 {
+ label = strings.Join(args[2:], " ")
+ } else {
+ scheme := url.Scheme + "://"
+ if url.Scheme == "mailto" {
+ scheme = "mailto:"
+ }
+ label = strings.Replace(url.String(), scheme, "", 1)
+ }
+ return Link{url, label}, nil
+}
+
+func renderLink(href, text string) string {
+ text = font(text)
+ if text == "" {
+ text = href
+ }
+ // Open links in new window only when their url is absolute.
+ target := "_blank"
+ if u, err := url.Parse(href); err != nil {
+ log.Println("renderLink parsing url:", err)
+ } else if !u.IsAbs() || u.Scheme == "javascript" {
+ target = "_self"
+ }
+
+ return fmt.Sprintf(`<a href="%s" target="%s">%s</a>`, href, target, text)
+}
+
+// parseInlineLink parses an inline link at the start of s, and returns
+// a rendered HTML link and the total length of the raw inline link.
+// If no inline link is present, it returns all zeroes.
+func parseInlineLink(s string) (link string, length int) {
+ if !strings.HasPrefix(s, "[[") {
+ return
+ }
+ end := strings.Index(s, "]]")
+ if end == -1 {
+ return
+ }
+ urlEnd := strings.Index(s, "]")
+ rawURL := s[2:urlEnd]
+ const badURLChars = `<>"{}|\^[] ` + "`" // per RFC2396 section 2.4.3
+ if strings.ContainsAny(rawURL, badURLChars) {
+ return
+ }
+ if urlEnd == end {
+ simpleUrl := ""
+ url, err := url.Parse(rawURL)
+ if err == nil {
+ // If the URL is http://foo.com, drop the http://
+ // In other words, render [[http://golang.org]] as:
+ // <a href="http://golang.org">golang.org</a>
+ if strings.HasPrefix(rawURL, url.Scheme+"://") {
+ simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+"://")
+ } else if strings.HasPrefix(rawURL, url.Scheme+":") {
+ simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+":")
+ }
+ }
+ return renderLink(rawURL, simpleUrl), end + 2
+ }
+ if s[urlEnd:urlEnd+2] != "][" {
+ return
+ }
+ text := s[urlEnd+2 : end]
+ return renderLink(rawURL, text), end + 2
+}
diff --git a/vendor/golang.org/x/tools/present/link_test.go b/vendor/golang.org/x/tools/present/link_test.go
new file mode 100644
index 0000000..334e72b
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/link_test.go
@@ -0,0 +1,40 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import "testing"
+
+func TestInlineParsing(t *testing.T) {
+ var tests = []struct {
+ in string
+ link string
+ text string
+ length int
+ }{
+ {"[[http://golang.org]]", "http://golang.org", "golang.org", 21},
+ {"[[http://golang.org][]]", "http://golang.org", "http://golang.org", 23},
+ {"[[http://golang.org]] this is ignored", "http://golang.org", "golang.org", 21},
+ {"[[http://golang.org][link]]", "http://golang.org", "link", 27},
+ {"[[http://golang.org][two words]]", "http://golang.org", "two words", 32},
+ {"[[http://golang.org][*link*]]", "http://golang.org", "<b>link</b>", 29},
+ {"[[http://bad[url]]", "", "", 0},
+ {"[[http://golang.org][a [[link]] ]]", "http://golang.org", "a [[link", 31},
+ {"[[http:// *spaces* .com]]", "", "", 0},
+ {"[[http://bad`char.com]]", "", "", 0},
+ {" [[http://google.com]]", "", "", 0},
+ {"[[mailto:gopher@golang.org][Gopher]]", "mailto:gopher@golang.org", "Gopher", 36},
+ {"[[mailto:gopher@golang.org]]", "mailto:gopher@golang.org", "gopher@golang.org", 28},
+ }
+
+ for i, test := range tests {
+ link, length := parseInlineLink(test.in)
+ if length == 0 && test.length == 0 {
+ continue
+ }
+ if a := renderLink(test.link, test.text); length != test.length || link != a {
+ t.Errorf("#%d: parseInlineLink(%q):\ngot\t%q, %d\nwant\t%q, %d", i, test.in, link, length, a, test.length)
+ }
+ }
+}
diff --git a/vendor/golang.org/x/tools/present/parse.go b/vendor/golang.org/x/tools/present/parse.go
new file mode 100644
index 0000000..d7289db
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/parse.go
@@ -0,0 +1,559 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/url"
+ "regexp"
+ "strings"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+var (
+ parsers = make(map[string]ParseFunc)
+ funcs = template.FuncMap{}
+)
+
+// Template returns an empty template with the action functions in its FuncMap.
+func Template() *template.Template {
+ return template.New("").Funcs(funcs)
+}
+
+// Render renders the doc to the given writer using the provided template.
+func (d *Doc) Render(w io.Writer, t *template.Template) error {
+ data := struct {
+ *Doc
+ Template *template.Template
+ PlayEnabled bool
+ NotesEnabled bool
+ }{d, t, PlayEnabled, NotesEnabled}
+ return t.ExecuteTemplate(w, "root", data)
+}
+
+// Render renders the section to the given writer using the provided template.
+func (s *Section) Render(w io.Writer, t *template.Template) error {
+ data := struct {
+ *Section
+ Template *template.Template
+ PlayEnabled bool
+ }{s, t, PlayEnabled}
+ return t.ExecuteTemplate(w, "section", data)
+}
+
+type ParseFunc func(ctx *Context, fileName string, lineNumber int, inputLine string) (Elem, error)
+
+// Register binds the named action, which does not begin with a period, to the
+// specified parser to be invoked when the name, with a period, appears in the
+// present input text.
+func Register(name string, parser ParseFunc) {
+ if len(name) == 0 || name[0] == ';' {
+ panic("bad name in Register: " + name)
+ }
+ parsers["."+name] = parser
+}
+
+// Doc represents an entire document.
+type Doc struct {
+ Title string
+ Subtitle string
+ Time time.Time
+ Authors []Author
+ TitleNotes []string
+ Sections []Section
+ Tags []string
+}
+
+// Author represents the person who wrote and/or is presenting the document.
+type Author struct {
+ Elem []Elem
+}
+
+// TextElem returns the first text elements of the author details.
+// This is used to display the author' name, job title, and company
+// without the contact details.
+func (p *Author) TextElem() (elems []Elem) {
+ for _, el := range p.Elem {
+ if _, ok := el.(Text); !ok {
+ break
+ }
+ elems = append(elems, el)
+ }
+ return
+}
+
+// Section represents a section of a document (such as a presentation slide)
+// comprising a title and a list of elements.
+type Section struct {
+ Number []int
+ Title string
+ Elem []Elem
+ Notes []string
+ Classes []string
+ Styles []string
+}
+
+// HTMLAttributes for the section
+func (s Section) HTMLAttributes() template.HTMLAttr {
+ if len(s.Classes) == 0 && len(s.Styles) == 0 {
+ return ""
+ }
+
+ var class string
+ if len(s.Classes) > 0 {
+ class = fmt.Sprintf(`class=%q`, strings.Join(s.Classes, " "))
+ }
+ var style string
+ if len(s.Styles) > 0 {
+ style = fmt.Sprintf(`style=%q`, strings.Join(s.Styles, " "))
+ }
+ return template.HTMLAttr(strings.Join([]string{class, style}, " "))
+}
+
+// Sections contained within the section.
+func (s Section) Sections() (sections []Section) {
+ for _, e := range s.Elem {
+ if section, ok := e.(Section); ok {
+ sections = append(sections, section)
+ }
+ }
+ return
+}
+
+// Level returns the level of the given section.
+// The document title is level 1, main section 2, etc.
+func (s Section) Level() int {
+ return len(s.Number) + 1
+}
+
+// FormattedNumber returns a string containing the concatenation of the
+// numbers identifying a Section.
+func (s Section) FormattedNumber() string {
+ b := &bytes.Buffer{}
+ for _, n := range s.Number {
+ fmt.Fprintf(b, "%v.", n)
+ }
+ return b.String()
+}
+
+func (s Section) TemplateName() string { return "section" }
+
+// Elem defines the interface for a present element. That is, something that
+// can provide the name of the template used to render the element.
+type Elem interface {
+ TemplateName() string
+}
+
+// renderElem implements the elem template function, used to render
+// sub-templates.
+func renderElem(t *template.Template, e Elem) (template.HTML, error) {
+ var data interface{} = e
+ if s, ok := e.(Section); ok {
+ data = struct {
+ Section
+ Template *template.Template
+ }{s, t}
+ }
+ return execTemplate(t, e.TemplateName(), data)
+}
+
+func init() {
+ funcs["elem"] = renderElem
+}
+
+// execTemplate is a helper to execute a template and return the output as a
+// template.HTML value.
+func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) {
+ b := new(bytes.Buffer)
+ err := t.ExecuteTemplate(b, name, data)
+ if err != nil {
+ return "", err
+ }
+ return template.HTML(b.String()), nil
+}
+
+// Text represents an optionally preformatted paragraph.
+type Text struct {
+ Lines []string
+ Pre bool
+}
+
+func (t Text) TemplateName() string { return "text" }
+
+// List represents a bulleted list.
+type List struct {
+ Bullet []string
+}
+
+func (l List) TemplateName() string { return "list" }
+
+// Lines is a helper for parsing line-based input.
+type Lines struct {
+ line int // 0 indexed, so has 1-indexed number of last line returned
+ text []string
+}
+
+func readLines(r io.Reader) (*Lines, error) {
+ var lines []string
+ s := bufio.NewScanner(r)
+ for s.Scan() {
+ lines = append(lines, s.Text())
+ }
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+ return &Lines{0, lines}, nil
+}
+
+func (l *Lines) next() (text string, ok bool) {
+ for {
+ current := l.line
+ l.line++
+ if current >= len(l.text) {
+ return "", false
+ }
+ text = l.text[current]
+ // Lines starting with # are comments.
+ if len(text) == 0 || text[0] != '#' {
+ ok = true
+ break
+ }
+ }
+ return
+}
+
+func (l *Lines) back() {
+ l.line--
+}
+
+func (l *Lines) nextNonEmpty() (text string, ok bool) {
+ for {
+ text, ok = l.next()
+ if !ok {
+ return
+ }
+ if len(text) > 0 {
+ break
+ }
+ }
+ return
+}
+
+// A Context specifies the supporting context for parsing a presentation.
+type Context struct {
+ // ReadFile reads the file named by filename and returns the contents.
+ ReadFile func(filename string) ([]byte, error)
+}
+
+// ParseMode represents flags for the Parse function.
+type ParseMode int
+
+const (
+ // If set, parse only the title and subtitle.
+ TitlesOnly ParseMode = 1
+)
+
+// Parse parses a document from r.
+func (ctx *Context) Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) {
+ doc := new(Doc)
+ lines, err := readLines(r)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := lines.line; i < len(lines.text); i++ {
+ if strings.HasPrefix(lines.text[i], "*") {
+ break
+ }
+
+ if isSpeakerNote(lines.text[i]) {
+ doc.TitleNotes = append(doc.TitleNotes, lines.text[i][2:])
+ }
+ }
+
+ err = parseHeader(doc, lines)
+ if err != nil {
+ return nil, err
+ }
+ if mode&TitlesOnly != 0 {
+ return doc, nil
+ }
+
+ // Authors
+ if doc.Authors, err = parseAuthors(lines); err != nil {
+ return nil, err
+ }
+ // Sections
+ if doc.Sections, err = parseSections(ctx, name, lines, []int{}); err != nil {
+ return nil, err
+ }
+ return doc, nil
+}
+
+// Parse parses a document from r. Parse reads assets used by the presentation
+// from the file system using ioutil.ReadFile.
+func Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) {
+ ctx := Context{ReadFile: ioutil.ReadFile}
+ return ctx.Parse(r, name, mode)
+}
+
+// isHeading matches any section heading.
+var isHeading = regexp.MustCompile(`^\*+ `)
+
+// lesserHeading returns true if text is a heading of a lesser or equal level
+// than that denoted by prefix.
+func lesserHeading(text, prefix string) bool {
+ return isHeading.MatchString(text) && !strings.HasPrefix(text, prefix+"*")
+}
+
+// parseSections parses Sections from lines for the section level indicated by
+// number (a nil number indicates the top level).
+func parseSections(ctx *Context, name string, lines *Lines, number []int) ([]Section, error) {
+ var sections []Section
+ for i := 1; ; i++ {
+ // Next non-empty line is title.
+ text, ok := lines.nextNonEmpty()
+ for ok && text == "" {
+ text, ok = lines.next()
+ }
+ if !ok {
+ break
+ }
+ prefix := strings.Repeat("*", len(number)+1)
+ if !strings.HasPrefix(text, prefix+" ") {
+ lines.back()
+ break
+ }
+ section := Section{
+ Number: append(append([]int{}, number...), i),
+ Title: text[len(prefix)+1:],
+ }
+ text, ok = lines.nextNonEmpty()
+ for ok && !lesserHeading(text, prefix) {
+ var e Elem
+ r, _ := utf8.DecodeRuneInString(text)
+ switch {
+ case unicode.IsSpace(r):
+ i := strings.IndexFunc(text, func(r rune) bool {
+ return !unicode.IsSpace(r)
+ })
+ if i < 0 {
+ break
+ }
+ indent := text[:i]
+ var s []string
+ for ok && (strings.HasPrefix(text, indent) || text == "") {
+ if text != "" {
+ text = text[i:]
+ }
+ s = append(s, text)
+ text, ok = lines.next()
+ }
+ lines.back()
+ pre := strings.Join(s, "\n")
+ pre = strings.Replace(pre, "\t", " ", -1) // browsers treat tabs badly
+ pre = strings.TrimRightFunc(pre, unicode.IsSpace)
+ e = Text{Lines: []string{pre}, Pre: true}
+ case strings.HasPrefix(text, "- "):
+ var b []string
+ for ok && strings.HasPrefix(text, "- ") {
+ b = append(b, text[2:])
+ text, ok = lines.next()
+ }
+ lines.back()
+ e = List{Bullet: b}
+ case isSpeakerNote(text):
+ section.Notes = append(section.Notes, text[2:])
+ case strings.HasPrefix(text, prefix+"* "):
+ lines.back()
+ subsecs, err := parseSections(ctx, name, lines, section.Number)
+ if err != nil {
+ return nil, err
+ }
+ for _, ss := range subsecs {
+ section.Elem = append(section.Elem, ss)
+ }
+ case strings.HasPrefix(text, "."):
+ args := strings.Fields(text)
+ if args[0] == ".background" {
+ section.Classes = append(section.Classes, "background")
+ section.Styles = append(section.Styles, "background-image: url('"+args[1]+"')")
+ break
+ }
+ parser := parsers[args[0]]
+ if parser == nil {
+ return nil, fmt.Errorf("%s:%d: unknown command %q\n", name, lines.line, text)
+ }
+ t, err := parser(ctx, name, lines.line, text)
+ if err != nil {
+ return nil, err
+ }
+ e = t
+ default:
+ var l []string
+ for ok && strings.TrimSpace(text) != "" {
+ if text[0] == '.' { // Command breaks text block.
+ lines.back()
+ break
+ }
+ if strings.HasPrefix(text, `\.`) { // Backslash escapes initial period.
+ text = text[1:]
+ }
+ l = append(l, text)
+ text, ok = lines.next()
+ }
+ if len(l) > 0 {
+ e = Text{Lines: l}
+ }
+ }
+ if e != nil {
+ section.Elem = append(section.Elem, e)
+ }
+ text, ok = lines.nextNonEmpty()
+ }
+ if isHeading.MatchString(text) {
+ lines.back()
+ }
+ sections = append(sections, section)
+ }
+ return sections, nil
+}
+
+func parseHeader(doc *Doc, lines *Lines) error {
+ var ok bool
+ // First non-empty line starts header.
+ doc.Title, ok = lines.nextNonEmpty()
+ if !ok {
+ return errors.New("unexpected EOF; expected title")
+ }
+ for {
+ text, ok := lines.next()
+ if !ok {
+ return errors.New("unexpected EOF")
+ }
+ if text == "" {
+ break
+ }
+ if isSpeakerNote(text) {
+ continue
+ }
+ const tagPrefix = "Tags:"
+ if strings.HasPrefix(text, tagPrefix) {
+ tags := strings.Split(text[len(tagPrefix):], ",")
+ for i := range tags {
+ tags[i] = strings.TrimSpace(tags[i])
+ }
+ doc.Tags = append(doc.Tags, tags...)
+ } else if t, ok := parseTime(text); ok {
+ doc.Time = t
+ } else if doc.Subtitle == "" {
+ doc.Subtitle = text
+ } else {
+ return fmt.Errorf("unexpected header line: %q", text)
+ }
+ }
+ return nil
+}
+
+func parseAuthors(lines *Lines) (authors []Author, err error) {
+ // This grammar demarcates authors with blanks.
+
+ // Skip blank lines.
+ if _, ok := lines.nextNonEmpty(); !ok {
+ return nil, errors.New("unexpected EOF")
+ }
+ lines.back()
+
+ var a *Author
+ for {
+ text, ok := lines.next()
+ if !ok {
+ return nil, errors.New("unexpected EOF")
+ }
+
+ // If we find a section heading, we're done.
+ if strings.HasPrefix(text, "* ") {
+ lines.back()
+ break
+ }
+
+ if isSpeakerNote(text) {
+ continue
+ }
+
+ // If we encounter a blank we're done with this author.
+ if a != nil && len(text) == 0 {
+ authors = append(authors, *a)
+ a = nil
+ continue
+ }
+ if a == nil {
+ a = new(Author)
+ }
+
+ // Parse the line. Those that
+ // - begin with @ are twitter names,
+ // - contain slashes are links, or
+ // - contain an @ symbol are an email address.
+ // The rest is just text.
+ var el Elem
+ switch {
+ case strings.HasPrefix(text, "@"):
+ el = parseURL("http://twitter.com/" + text[1:])
+ case strings.Contains(text, ":"):
+ el = parseURL(text)
+ case strings.Contains(text, "@"):
+ el = parseURL("mailto:" + text)
+ }
+ if l, ok := el.(Link); ok {
+ l.Label = text
+ el = l
+ }
+ if el == nil {
+ el = Text{Lines: []string{text}}
+ }
+ a.Elem = append(a.Elem, el)
+ }
+ if a != nil {
+ authors = append(authors, *a)
+ }
+ return authors, nil
+}
+
+func parseURL(text string) Elem {
+ u, err := url.Parse(text)
+ if err != nil {
+ log.Printf("Parse(%q): %v", text, err)
+ return nil
+ }
+ return Link{URL: u}
+}
+
+func parseTime(text string) (t time.Time, ok bool) {
+ t, err := time.Parse("15:04 2 Jan 2006", text)
+ if err == nil {
+ return t, true
+ }
+ t, err = time.Parse("2 Jan 2006", text)
+ if err == nil {
+ // at 11am UTC it is the same date everywhere
+ t = t.Add(time.Hour * 11)
+ return t, true
+ }
+ return time.Time{}, false
+}
+
+func isSpeakerNote(s string) bool {
+ return strings.HasPrefix(s, ": ")
+}
diff --git a/vendor/golang.org/x/tools/present/style.go b/vendor/golang.org/x/tools/present/style.go
new file mode 100644
index 0000000..e2c228e
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/style.go
@@ -0,0 +1,167 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "bytes"
+ "html"
+ "html/template"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+/*
+ Fonts are demarcated by an initial and final char bracketing a
+ space-delimited word, plus possibly some terminal punctuation.
+ The chars are
+ _ for italic
+ * for bold
+ ` (back quote) for fixed width.
+ Inner appearances of the char become spaces. For instance,
+ _this_is_italic_!
+ becomes
+ <i>this is italic</i>!
+*/
+
+func init() {
+ funcs["style"] = Style
+}
+
+// Style returns s with HTML entities escaped and font indicators turned into
+// HTML font tags.
+func Style(s string) template.HTML {
+ return template.HTML(font(html.EscapeString(s)))
+}
+
+// font returns s with font indicators turned into HTML font tags.
+func font(s string) string {
+ if !strings.ContainsAny(s, "[`_*") {
+ return s
+ }
+ words := split(s)
+ var b bytes.Buffer
+Word:
+ for w, word := range words {
+ if len(word) < 2 {
+ continue Word
+ }
+ if link, _ := parseInlineLink(word); link != "" {
+ words[w] = link
+ continue Word
+ }
+ const marker = "_*`"
+ // Initial punctuation is OK but must be peeled off.
+ first := strings.IndexAny(word, marker)
+ if first == -1 {
+ continue Word
+ }
+ // Opening marker must be at the beginning of the token or else preceded by punctuation.
+ if first != 0 {
+ r, _ := utf8.DecodeLastRuneInString(word[:first])
+ if !unicode.IsPunct(r) {
+ continue Word
+ }
+ }
+ open, word := word[:first], word[first:]
+ char := word[0] // ASCII is OK.
+ close := ""
+ switch char {
+ default:
+ continue Word
+ case '_':
+ open += "<i>"
+ close = "</i>"
+ case '*':
+ open += "<b>"
+ close = "</b>"
+ case '`':
+ open += "<code>"
+ close = "</code>"
+ }
+ // Closing marker must be at the end of the token or else followed by punctuation.
+ last := strings.LastIndex(word, word[:1])
+ if last == 0 {
+ continue Word
+ }
+ if last+1 != len(word) {
+ r, _ := utf8.DecodeRuneInString(word[last+1:])
+ if !unicode.IsPunct(r) {
+ continue Word
+ }
+ }
+ head, tail := word[:last+1], word[last+1:]
+ b.Reset()
+ b.WriteString(open)
+ var wid int
+ for i := 1; i < len(head)-1; i += wid {
+ var r rune
+ r, wid = utf8.DecodeRuneInString(head[i:])
+ if r != rune(char) {
+ // Ordinary character.
+ b.WriteRune(r)
+ continue
+ }
+ if head[i+1] != char {
+ // Inner char becomes space.
+ b.WriteRune(' ')
+ continue
+ }
+ // Doubled char becomes real char.
+ // Not worth worrying about "_x__".
+ b.WriteByte(char)
+ wid++ // Consumed two chars, both ASCII.
+ }
+ b.WriteString(close) // Write closing tag.
+ b.WriteString(tail) // Restore trailing punctuation.
+ words[w] = b.String()
+ }
+ return strings.Join(words, "")
+}
+
+// split is like strings.Fields but also returns the runs of spaces
+// and treats inline links as distinct words.
+func split(s string) []string {
+ var (
+ words = make([]string, 0, 10)
+ start = 0
+ )
+
+ // appendWord appends the string s[start:end] to the words slice.
+ // If the word contains the beginning of a link, the non-link portion
+ // of the word and the entire link are appended as separate words,
+ // and the start index is advanced to the end of the link.
+ appendWord := func(end int) {
+ if j := strings.Index(s[start:end], "[["); j > -1 {
+ if _, l := parseInlineLink(s[start+j:]); l > 0 {
+ // Append portion before link, if any.
+ if j > 0 {
+ words = append(words, s[start:start+j])
+ }
+ // Append link itself.
+ words = append(words, s[start+j:start+j+l])
+ // Advance start index to end of link.
+ start = start + j + l
+ return
+ }
+ }
+ // No link; just add the word.
+ words = append(words, s[start:end])
+ start = end
+ }
+
+ wasSpace := false
+ for i, r := range s {
+ isSpace := unicode.IsSpace(r)
+ if i > start && isSpace != wasSpace {
+ appendWord(i)
+ }
+ wasSpace = isSpace
+ }
+ for start < len(s) {
+ appendWord(len(s))
+ }
+ return words
+}
diff --git a/vendor/golang.org/x/tools/present/style_test.go b/vendor/golang.org/x/tools/present/style_test.go
new file mode 100644
index 0000000..cef5a62
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/style_test.go
@@ -0,0 +1,124 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestSplit(t *testing.T) {
+ var tests = []struct {
+ in string
+ out []string
+ }{
+ {"", []string{}},
+ {" ", []string{" "}},
+ {"abc", []string{"abc"}},
+ {"abc def", []string{"abc", " ", "def"}},
+ {"abc def ", []string{"abc", " ", "def", " "}},
+ {"hey [[http://golang.org][Gophers]] around",
+ []string{"hey", " ", "[[http://golang.org][Gophers]]", " ", "around"}},
+ {"A [[http://golang.org/doc][two words]] link",
+ []string{"A", " ", "[[http://golang.org/doc][two words]]", " ", "link"}},
+ {"Visit [[http://golang.org/doc]] now",
+ []string{"Visit", " ", "[[http://golang.org/doc]]", " ", "now"}},
+ {"not [[http://golang.org/doc][a [[link]] ]] around",
+ []string{"not", " ", "[[http://golang.org/doc][a [[link]]", " ", "]]", " ", "around"}},
+ {"[[http://golang.org][foo bar]]",
+ []string{"[[http://golang.org][foo bar]]"}},
+ {"ends with [[http://golang.org][link]]",
+ []string{"ends", " ", "with", " ", "[[http://golang.org][link]]"}},
+ {"my talk ([[http://talks.golang.org/][slides here]])",
+ []string{"my", " ", "talk", " ", "(", "[[http://talks.golang.org/][slides here]]", ")"}},
+ }
+ for _, test := range tests {
+ out := split(test.in)
+ if !reflect.DeepEqual(out, test.out) {
+ t.Errorf("split(%q):\ngot\t%q\nwant\t%q", test.in, out, test.out)
+ }
+ }
+}
+
+func TestFont(t *testing.T) {
+ var tests = []struct {
+ in string
+ out string
+ }{
+ {"", ""},
+ {" ", " "},
+ {"\tx", "\tx"},
+ {"_a_", "<i>a</i>"},
+ {"*a*", "<b>a</b>"},
+ {"`a`", "<code>a</code>"},
+ {"_a_b_", "<i>a b</i>"},
+ {"_a__b_", "<i>a_b</i>"},
+ {"_a___b_", "<i>a_ b</i>"},
+ {"*a**b*?", "<b>a*b</b>?"},
+ {"_a_<>_b_.", "<i>a <> b</i>."},
+ {"(_a_)", "(<i>a</i>)"},
+ {"((_a_), _b_, _c_).", "((<i>a</i>), <i>b</i>, <i>c</i>)."},
+ {"(_a)", "(_a)"},
+ {"(_a)", "(_a)"},
+ {"_Why_use_scoped__ptr_? Use plain ***ptr* instead.", "<i>Why use scoped_ptr</i>? Use plain <b>*ptr</b> instead."},
+ {"_hey_ [[http://golang.org][*Gophers*]] *around*",
+ `<i>hey</i> <a href="http://golang.org" target="_blank"><b>Gophers</b></a> <b>around</b>`},
+ {"_hey_ [[http://golang.org][so _many_ *Gophers*]] *around*",
+ `<i>hey</i> <a href="http://golang.org" target="_blank">so <i>many</i> <b>Gophers</b></a> <b>around</b>`},
+ {"Visit [[http://golang.org]] now",
+ `Visit <a href="http://golang.org" target="_blank">golang.org</a> now`},
+ {"my talk ([[http://talks.golang.org/][slides here]])",
+ `my talk (<a href="http://talks.golang.org/" target="_blank">slides here</a>)`},
+ {"Markup—_especially_italic_text_—can easily be overused.",
+ `Markup—<i>especially italic text</i>—can easily be overused.`},
+ {"`go`get`'s codebase", // ascii U+0027 ' before s
+ `<code>go get</code>'s codebase`},
+ {"`go`get`’s codebase", // unicode right single quote U+2019 ’ before s
+ `<code>go get</code>’s codebase`},
+ {"a_variable_name",
+ `a_variable_name`},
+ }
+ for _, test := range tests {
+ out := font(test.in)
+ if out != test.out {
+ t.Errorf("font(%q):\ngot\t%q\nwant\t%q", test.in, out, test.out)
+ }
+ }
+}
+
+func TestStyle(t *testing.T) {
+ var tests = []struct {
+ in string
+ out string
+ }{
+ {"", ""},
+ {" ", " "},
+ {"\tx", "\tx"},
+ {"_a_", "<i>a</i>"},
+ {"*a*", "<b>a</b>"},
+ {"`a`", "<code>a</code>"},
+ {"_a_b_", "<i>a b</i>"},
+ {"_a__b_", "<i>a_b</i>"},
+ {"_a___b_", "<i>a_ b</i>"},
+ {"*a**b*?", "<b>a*b</b>?"},
+ {"_a_<>_b_.", "<i>a &lt;&gt; b</i>."},
+ {"(_a_<>_b_)", "(<i>a &lt;&gt; b</i>)"},
+ {"((_a_), _b_, _c_).", "((<i>a</i>), <i>b</i>, <i>c</i>)."},
+ {"(_a)", "(_a)"},
+ }
+ for _, test := range tests {
+ out := string(Style(test.in))
+ if out != test.out {
+ t.Errorf("style(%q):\ngot\t%q\nwant\t%q", test.in, out, test.out)
+ }
+ }
+}
+
+func ExampleStyle() {
+ const s = "*Gophers* are _clearly_ > *cats*!"
+ fmt.Println(Style(s))
+ // Output: <b>Gophers</b> are <i>clearly</i> &gt; <b>cats</b>!
+}
diff --git a/vendor/golang.org/x/tools/present/video.go b/vendor/golang.org/x/tools/present/video.go
new file mode 100644
index 0000000..913822e
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/video.go
@@ -0,0 +1,51 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "strings"
+)
+
+func init() {
+ Register("video", parseVideo)
+}
+
+type Video struct {
+ URL string
+ SourceType string
+ Width int
+ Height int
+}
+
+func (v Video) TemplateName() string { return "video" }
+
+func parseVideo(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ vid := Video{URL: args[1], SourceType: args[2]}
+ a, err := parseArgs(fileName, lineno, args[3:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ // If a parameter is empty (underscore) or invalid
+ // leave the field set to zero. The "video" action
+ // template will then omit that vid tag attribute and
+ // the browser will calculate the value to preserve
+ // the aspect ratio.
+ if v, ok := a[0].(int); ok {
+ vid.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ vid.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect video invocation: %q", text)
+ }
+ return vid, nil
+}