From f69b3fb4237344ce861d7271fec6949d4702402e Mon Sep 17 00:00:00 2001
From: Christine Dodrill
Date: Fri, 5 Oct 2018 14:13:52 +0000
Subject: vendor dependencies
---
vendor/github.com/jaytaylor/html2text/.gitignore | 24 +
vendor/github.com/jaytaylor/html2text/.travis.yml | 14 +
vendor/github.com/jaytaylor/html2text/LICENSE | 22 +
vendor/github.com/jaytaylor/html2text/README.md | 137 ++++
vendor/github.com/jaytaylor/html2text/html2text.go | 473 ++++++++++++
.../github.com/olekukonko/tablewriter/.gitignore | 15 +
.../github.com/olekukonko/tablewriter/.travis.yml | 14 +
.../github.com/olekukonko/tablewriter/LICENCE.md | 19 +
vendor/github.com/olekukonko/tablewriter/README.md | 277 +++++++
vendor/github.com/olekukonko/tablewriter/csv.go | 52 ++
vendor/github.com/olekukonko/tablewriter/table.go | 839 +++++++++++++++++++++
.../olekukonko/tablewriter/table_with_color.go | 134 ++++
vendor/github.com/olekukonko/tablewriter/util.go | 93 +++
vendor/github.com/olekukonko/tablewriter/wrap.go | 99 +++
vendor/github.com/ssor/bom/.travis.yml | 14 +
vendor/github.com/ssor/bom/LICENSE | 21 +
vendor/github.com/ssor/bom/README.md | 23 +
vendor/github.com/ssor/bom/bom.go | 34 +
18 files changed, 2304 insertions(+)
create mode 100644 vendor/github.com/jaytaylor/html2text/.gitignore
create mode 100644 vendor/github.com/jaytaylor/html2text/.travis.yml
create mode 100644 vendor/github.com/jaytaylor/html2text/LICENSE
create mode 100644 vendor/github.com/jaytaylor/html2text/README.md
create mode 100644 vendor/github.com/jaytaylor/html2text/html2text.go
create mode 100644 vendor/github.com/olekukonko/tablewriter/.gitignore
create mode 100644 vendor/github.com/olekukonko/tablewriter/.travis.yml
create mode 100644 vendor/github.com/olekukonko/tablewriter/LICENCE.md
create mode 100644 vendor/github.com/olekukonko/tablewriter/README.md
create mode 100644 vendor/github.com/olekukonko/tablewriter/csv.go
create mode 100644 vendor/github.com/olekukonko/tablewriter/table.go
create mode 100644 vendor/github.com/olekukonko/tablewriter/table_with_color.go
create mode 100644 vendor/github.com/olekukonko/tablewriter/util.go
create mode 100644 vendor/github.com/olekukonko/tablewriter/wrap.go
create mode 100644 vendor/github.com/ssor/bom/.travis.yml
create mode 100644 vendor/github.com/ssor/bom/LICENSE
create mode 100644 vendor/github.com/ssor/bom/README.md
create mode 100644 vendor/github.com/ssor/bom/bom.go
(limited to 'vendor/github.com')
diff --git a/vendor/github.com/jaytaylor/html2text/.gitignore b/vendor/github.com/jaytaylor/html2text/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/jaytaylor/html2text/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/jaytaylor/html2text/.travis.yml b/vendor/github.com/jaytaylor/html2text/.travis.yml
new file mode 100644
index 0000000..6c7f48e
--- /dev/null
+++ b/vendor/github.com/jaytaylor/html2text/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+go:
+ - tip
+ - 1.8
+ - 1.7
+ - 1.6
+ - 1.5
+ - 1.4
+ - 1.3
+ - 1.2
+notifications:
+ email:
+ on_success: change
+ on_failure: always
diff --git a/vendor/github.com/jaytaylor/html2text/LICENSE b/vendor/github.com/jaytaylor/html2text/LICENSE
new file mode 100644
index 0000000..24dc4ab
--- /dev/null
+++ b/vendor/github.com/jaytaylor/html2text/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jay Taylor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/jaytaylor/html2text/README.md b/vendor/github.com/jaytaylor/html2text/README.md
new file mode 100644
index 0000000..6b2494e
--- /dev/null
+++ b/vendor/github.com/jaytaylor/html2text/README.md
@@ -0,0 +1,137 @@
+# html2text
+
+[](https://godoc.org/github.com/jaytaylor/html2text)
+[](https://travis-ci.org/jaytaylor/html2text)
+[](https://goreportcard.com/report/github.com/jaytaylor/html2text)
+
+### Converts HTML into text of the markdown-flavored variety
+
+
+## Introduction
+
+Ensure your emails are readable by all!
+
+Turns HTML into raw text, useful for sending fancy HTML emails with an equivalently nicely formatted TXT document as a fallback (e.g. for people who don't allow HTML emails or have other display issues).
+
+html2text is a simple golang package for rendering HTML into plaintext.
+
+There are still lots of improvements to be had, but FWIW this has worked fine for my [basic] HTML-2-text needs.
+
+It requires go 1.x or newer ;)
+
+
+## Download the package
+
+```bash
+go get jaytaylor.com/html2text
+```
+
+## Example usage
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "jaytaylor.com/html2text"
+)
+
+func main() {
+ inputHTML := `
+
+
+ My Mega Service
+
+
+
+
+
+
+
+
+
+ Welcome to your new account on my service!
+
+
+ Here is some more information:
+
+
+
+
+
+
+ Header 1 Header 2
+
+
+ Footer 1 Footer 2
+
+
+ Row 1 Col 1 Row 1 Col 2
+ Row 2 Col 1 Row 2 Col 2
+
+
+
+`
+
+ text, err := html2text.FromString(inputHTML, html2text.Options{PrettyTables: true})
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(text)
+}
+```
+
+Output:
+```
+Mega Service ( http://jaytaylor.com/ )
+
+******************************************
+Welcome to your new account on my service!
+******************************************
+
+Here is some more information:
+
+* Link 1: Example.com ( https://example.com )
+* Link 2: Example2.com ( https://example2.com )
+* Something else
+
++-------------+-------------+
+| HEADER 1 | HEADER 2 |
++-------------+-------------+
+| Row 1 Col 1 | Row 1 Col 2 |
+| Row 2 Col 1 | Row 2 Col 2 |
++-------------+-------------+
+| FOOTER 1 | FOOTER 2 |
++-------------+-------------+
+```
+
+
+## Unit-tests
+
+Running the unit-tests is straightforward and standard:
+
+```bash
+go test
+```
+
+
+# License
+
+Permissive MIT license.
+
+
+## Contact
+
+You are more than welcome to open issues and send pull requests if you find a bug or want a new feature.
+
+If you appreciate this library please feel free to drop me a line and tell me! It's always nice to hear from people who have benefitted from my work.
+
+Email: jay at (my github username).com
+
+Twitter: [@jtaylor](https://twitter.com/jtaylor)
+
diff --git a/vendor/github.com/jaytaylor/html2text/html2text.go b/vendor/github.com/jaytaylor/html2text/html2text.go
new file mode 100644
index 0000000..fa36990
--- /dev/null
+++ b/vendor/github.com/jaytaylor/html2text/html2text.go
@@ -0,0 +1,473 @@
+package html2text
+
+import (
+ "bytes"
+ "io"
+ "regexp"
+ "strings"
+ "unicode"
+
+ "github.com/olekukonko/tablewriter"
+ "github.com/ssor/bom"
+ "golang.org/x/net/html"
+ "golang.org/x/net/html/atom"
+)
+
+// Options provide toggles and overrides to control specific rendering behaviors.
+type Options struct {
+ PrettyTables bool // Turns on pretty ASCII rendering for table elements.
+ OmitLinks bool // Turns on omitting links
+}
+
+// FromHTMLNode renders text output from a pre-parsed HTML document.
+func FromHTMLNode(doc *html.Node, o ...Options) (string, error) {
+ var options Options
+ if len(o) > 0 {
+ options = o[0]
+ }
+
+ ctx := textifyTraverseContext{
+ buf: bytes.Buffer{},
+ options: options,
+ }
+ if err := ctx.traverse(doc); err != nil {
+ return "", err
+ }
+
+ text := strings.TrimSpace(newlineRe.ReplaceAllString(
+ strings.Replace(ctx.buf.String(), "\n ", "\n", -1), "\n\n"),
+ )
+ return text, nil
+}
+
+// FromReader renders text output after parsing HTML for the specified
+// io.Reader.
+func FromReader(reader io.Reader, options ...Options) (string, error) {
+ newReader, err := bom.NewReaderWithoutBom(reader)
+ if err != nil {
+ return "", err
+ }
+ doc, err := html.Parse(newReader)
+ if err != nil {
+ return "", err
+ }
+ return FromHTMLNode(doc, options...)
+}
+
+// FromString parses HTML from the input string, then renders the text form.
+func FromString(input string, options ...Options) (string, error) {
+ bs := bom.CleanBom([]byte(input))
+ text, err := FromReader(bytes.NewReader(bs), options...)
+ if err != nil {
+ return "", err
+ }
+ return text, nil
+}
+
+var (
+ spacingRe = regexp.MustCompile(`[ \r\n\t]+`)
+ newlineRe = regexp.MustCompile(`\n\n+`)
+)
+
+// traverseTableCtx holds text-related context.
+type textifyTraverseContext struct {
+ buf bytes.Buffer
+
+ prefix string
+ tableCtx tableTraverseContext
+ options Options
+ endsWithSpace bool
+ justClosedDiv bool
+ blockquoteLevel int
+ lineLength int
+ isPre bool
+}
+
+// tableTraverseContext holds table ASCII-form related context.
+type tableTraverseContext struct {
+ header []string
+ body [][]string
+ footer []string
+ tmpRow int
+ isInFooter bool
+}
+
+func (tableCtx *tableTraverseContext) init() {
+ tableCtx.body = [][]string{}
+ tableCtx.header = []string{}
+ tableCtx.footer = []string{}
+ tableCtx.isInFooter = false
+ tableCtx.tmpRow = 0
+}
+
+func (ctx *textifyTraverseContext) handleElement(node *html.Node) error {
+ ctx.justClosedDiv = false
+
+ switch node.DataAtom {
+ case atom.Br:
+ return ctx.emit("\n")
+
+ case atom.H1, atom.H2, atom.H3:
+ subCtx := textifyTraverseContext{}
+ if err := subCtx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ str := subCtx.buf.String()
+ dividerLen := 0
+ for _, line := range strings.Split(str, "\n") {
+ if lineLen := len([]rune(line)); lineLen-1 > dividerLen {
+ dividerLen = lineLen - 1
+ }
+ }
+ var divider string
+ if node.DataAtom == atom.H1 {
+ divider = strings.Repeat("*", dividerLen)
+ } else {
+ divider = strings.Repeat("-", dividerLen)
+ }
+
+ if node.DataAtom == atom.H3 {
+ return ctx.emit("\n\n" + str + "\n" + divider + "\n\n")
+ }
+ return ctx.emit("\n\n" + divider + "\n" + str + "\n" + divider + "\n\n")
+
+ case atom.Blockquote:
+ ctx.blockquoteLevel++
+ ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel) + " "
+ if err := ctx.emit("\n"); err != nil {
+ return err
+ }
+ if ctx.blockquoteLevel == 1 {
+ if err := ctx.emit("\n"); err != nil {
+ return err
+ }
+ }
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ ctx.blockquoteLevel--
+ ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel)
+ if ctx.blockquoteLevel > 0 {
+ ctx.prefix += " "
+ }
+ return ctx.emit("\n\n")
+
+ case atom.Div:
+ if ctx.lineLength > 0 {
+ if err := ctx.emit("\n"); err != nil {
+ return err
+ }
+ }
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ var err error
+ if !ctx.justClosedDiv {
+ err = ctx.emit("\n")
+ }
+ ctx.justClosedDiv = true
+ return err
+
+ case atom.Li:
+ if err := ctx.emit("* "); err != nil {
+ return err
+ }
+
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ return ctx.emit("\n")
+
+ case atom.B, atom.Strong:
+ subCtx := textifyTraverseContext{}
+ subCtx.endsWithSpace = true
+ if err := subCtx.traverseChildren(node); err != nil {
+ return err
+ }
+ str := subCtx.buf.String()
+ return ctx.emit("*" + str + "*")
+
+ case atom.A:
+ linkText := ""
+ // For simple link element content with single text node only, peek at the link text.
+ if node.FirstChild != nil && node.FirstChild.NextSibling == nil && node.FirstChild.Type == html.TextNode {
+ linkText = node.FirstChild.Data
+ }
+
+ // If image is the only child, take its alt text as the link text.
+ if img := node.FirstChild; img != nil && node.LastChild == img && img.DataAtom == atom.Img {
+ if altText := getAttrVal(img, "alt"); altText != "" {
+ if err := ctx.emit(altText); err != nil {
+ return err
+ }
+ }
+ } else if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ hrefLink := ""
+ if attrVal := getAttrVal(node, "href"); attrVal != "" {
+ attrVal = ctx.normalizeHrefLink(attrVal)
+ // Don't print link href if it matches link element content or if the link is empty.
+ if !ctx.options.OmitLinks && attrVal != "" && linkText != attrVal {
+ hrefLink = "( " + attrVal + " )"
+ }
+ }
+
+ return ctx.emit(hrefLink)
+
+ case atom.P, atom.Ul:
+ return ctx.paragraphHandler(node)
+
+ case atom.Table, atom.Tfoot, atom.Th, atom.Tr, atom.Td:
+ if ctx.options.PrettyTables {
+ return ctx.handleTableElement(node)
+ } else if node.DataAtom == atom.Table {
+ return ctx.paragraphHandler(node)
+ }
+ return ctx.traverseChildren(node)
+
+ case atom.Pre:
+ ctx.isPre = true
+ err := ctx.traverseChildren(node)
+ ctx.isPre = false
+ return err
+
+ case atom.Style, atom.Script, atom.Head:
+ // Ignore the subtree.
+ return nil
+
+ default:
+ return ctx.traverseChildren(node)
+ }
+}
+
+// paragraphHandler renders node children surrounded by double newlines.
+func (ctx *textifyTraverseContext) paragraphHandler(node *html.Node) error {
+ if err := ctx.emit("\n\n"); err != nil {
+ return err
+ }
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ return ctx.emit("\n\n")
+}
+
+// handleTableElement is only to be invoked when options.PrettyTables is active.
+func (ctx *textifyTraverseContext) handleTableElement(node *html.Node) error {
+ if !ctx.options.PrettyTables {
+ panic("handleTableElement invoked when PrettyTables not active")
+ }
+
+ switch node.DataAtom {
+ case atom.Table:
+ if err := ctx.emit("\n\n"); err != nil {
+ return err
+ }
+
+ // Re-intialize all table context.
+ ctx.tableCtx.init()
+
+ // Browse children, enriching context with table data.
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ buf := &bytes.Buffer{}
+ table := tablewriter.NewWriter(buf)
+ table.SetHeader(ctx.tableCtx.header)
+ table.SetFooter(ctx.tableCtx.footer)
+ table.AppendBulk(ctx.tableCtx.body)
+
+ // Render the table using ASCII.
+ table.Render()
+ if err := ctx.emit(buf.String()); err != nil {
+ return err
+ }
+
+ return ctx.emit("\n\n")
+
+ case atom.Tfoot:
+ ctx.tableCtx.isInFooter = true
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ ctx.tableCtx.isInFooter = false
+
+ case atom.Tr:
+ ctx.tableCtx.body = append(ctx.tableCtx.body, []string{})
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ ctx.tableCtx.tmpRow++
+
+ case atom.Th:
+ res, err := ctx.renderEachChild(node)
+ if err != nil {
+ return err
+ }
+
+ ctx.tableCtx.header = append(ctx.tableCtx.header, res)
+
+ case atom.Td:
+ res, err := ctx.renderEachChild(node)
+ if err != nil {
+ return err
+ }
+
+ if ctx.tableCtx.isInFooter {
+ ctx.tableCtx.footer = append(ctx.tableCtx.footer, res)
+ } else {
+ ctx.tableCtx.body[ctx.tableCtx.tmpRow] = append(ctx.tableCtx.body[ctx.tableCtx.tmpRow], res)
+ }
+
+ }
+ return nil
+}
+
+func (ctx *textifyTraverseContext) traverse(node *html.Node) error {
+ switch node.Type {
+ default:
+ return ctx.traverseChildren(node)
+
+ case html.TextNode:
+ var data string
+ if ctx.isPre {
+ data = node.Data
+ } else {
+ data = strings.Trim(spacingRe.ReplaceAllString(node.Data, " "), " ")
+ }
+ return ctx.emit(data)
+
+ case html.ElementNode:
+ return ctx.handleElement(node)
+ }
+}
+
+func (ctx *textifyTraverseContext) traverseChildren(node *html.Node) error {
+ for c := node.FirstChild; c != nil; c = c.NextSibling {
+ if err := ctx.traverse(c); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (ctx *textifyTraverseContext) emit(data string) error {
+ if data == "" {
+ return nil
+ }
+ var (
+ lines = ctx.breakLongLines(data)
+ err error
+ )
+ for _, line := range lines {
+ runes := []rune(line)
+ startsWithSpace := unicode.IsSpace(runes[0])
+ if !startsWithSpace && !ctx.endsWithSpace && !strings.HasPrefix(data, ".") {
+ if err = ctx.buf.WriteByte(' '); err != nil {
+ return err
+ }
+ ctx.lineLength++
+ }
+ ctx.endsWithSpace = unicode.IsSpace(runes[len(runes)-1])
+ for _, c := range line {
+ if _, err = ctx.buf.WriteString(string(c)); err != nil {
+ return err
+ }
+ ctx.lineLength++
+ if c == '\n' {
+ ctx.lineLength = 0
+ if ctx.prefix != "" {
+ if _, err = ctx.buf.WriteString(ctx.prefix); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+const maxLineLen = 74
+
+func (ctx *textifyTraverseContext) breakLongLines(data string) []string {
+ // Only break lines when in blockquotes.
+ if ctx.blockquoteLevel == 0 {
+ return []string{data}
+ }
+ var (
+ ret = []string{}
+ runes = []rune(data)
+ l = len(runes)
+ existing = ctx.lineLength
+ )
+ if existing >= maxLineLen {
+ ret = append(ret, "\n")
+ existing = 0
+ }
+ for l+existing > maxLineLen {
+ i := maxLineLen - existing
+ for i >= 0 && !unicode.IsSpace(runes[i]) {
+ i--
+ }
+ if i == -1 {
+ // No spaces, so go the other way.
+ i = maxLineLen - existing
+ for i < l && !unicode.IsSpace(runes[i]) {
+ i++
+ }
+ }
+ ret = append(ret, string(runes[:i])+"\n")
+ for i < l && unicode.IsSpace(runes[i]) {
+ i++
+ }
+ runes = runes[i:]
+ l = len(runes)
+ existing = 0
+ }
+ if len(runes) > 0 {
+ ret = append(ret, string(runes))
+ }
+ return ret
+}
+
+func (ctx *textifyTraverseContext) normalizeHrefLink(link string) string {
+ link = strings.TrimSpace(link)
+ link = strings.TrimPrefix(link, "mailto:")
+ return link
+}
+
+// renderEachChild visits each direct child of a node and collects the sequence of
+// textuual representaitons separated by a single newline.
+func (ctx *textifyTraverseContext) renderEachChild(node *html.Node) (string, error) {
+ buf := &bytes.Buffer{}
+ for c := node.FirstChild; c != nil; c = c.NextSibling {
+ s, err := FromHTMLNode(c, ctx.options)
+ if err != nil {
+ return "", err
+ }
+ if _, err = buf.WriteString(s); err != nil {
+ return "", err
+ }
+ if c.NextSibling != nil {
+ if err = buf.WriteByte('\n'); err != nil {
+ return "", err
+ }
+ }
+ }
+ return buf.String(), nil
+}
+
+func getAttrVal(node *html.Node, attrName string) string {
+ for _, attr := range node.Attr {
+ if attr.Key == attrName {
+ return attr.Val
+ }
+ }
+
+ return ""
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/.gitignore b/vendor/github.com/olekukonko/tablewriter/.gitignore
new file mode 100644
index 0000000..b66cec6
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/.gitignore
@@ -0,0 +1,15 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Go template
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
diff --git a/vendor/github.com/olekukonko/tablewriter/.travis.yml b/vendor/github.com/olekukonko/tablewriter/.travis.yml
new file mode 100644
index 0000000..9c64270
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+
+go:
+ - 1.1
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - 1.7
+ - 1.8
+ - 1.9
+ - "1.10"
+ - tip
diff --git a/vendor/github.com/olekukonko/tablewriter/LICENCE.md b/vendor/github.com/olekukonko/tablewriter/LICENCE.md
new file mode 100644
index 0000000..1fd8484
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/LICENCE.md
@@ -0,0 +1,19 @@
+Copyright (C) 2014 by Oleku Konko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/olekukonko/tablewriter/README.md b/vendor/github.com/olekukonko/tablewriter/README.md
new file mode 100644
index 0000000..d44d31a
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/README.md
@@ -0,0 +1,277 @@
+ASCII Table Writer
+=========
+
+[](https://travis-ci.org/olekukonko/tablewriter)
+[](https://sourcegraph.com/github.com/olekukonko/tablewriter)
+[](https://godoc.org/github.com/olekukonko/tablewriter)
+
+Generate ASCII table on the fly ... Installation is simple as
+
+ go get github.com/olekukonko/tablewriter
+
+
+#### Features
+- Automatic Padding
+- Support Multiple Lines
+- Supports Alignment
+- Support Custom Separators
+- Automatic Alignment of numbers & percentage
+- Write directly to http , file etc via `io.Writer`
+- Read directly from CSV file
+- Optional row line via `SetRowLine`
+- Normalise table header
+- Make CSV Headers optional
+- Enable or disable table border
+- Set custom footer support
+- Optional identical cells merging
+- Set custom caption
+- Optional reflowing of paragrpahs in multi-line cells.
+
+#### Example 1 - Basic
+```go
+data := [][]string{
+ []string{"A", "The Good", "500"},
+ []string{"B", "The Very very Bad Man", "288"},
+ []string{"C", "The Ugly", "120"},
+ []string{"D", "The Gopher", "800"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Name", "Sign", "Rating"})
+
+for _, v := range data {
+ table.Append(v)
+}
+table.Render() // Send output
+```
+
+##### Output 1
+```
++------+-----------------------+--------+
+| NAME | SIGN | RATING |
++------+-----------------------+--------+
+| A | The Good | 500 |
+| B | The Very very Bad Man | 288 |
+| C | The Ugly | 120 |
+| D | The Gopher | 800 |
++------+-----------------------+--------+
+```
+
+#### Example 2 - Without Border / Footer / Bulk Append
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "2233", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2233", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "2233", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
+table.SetBorder(false) // Set Border to false
+table.AppendBulk(data) // Add Bulk Data
+table.Render()
+```
+
+##### Output 2
+```
+
+ DATE | DESCRIPTION | CV2 | AMOUNT
++----------+--------------------------+-------+---------+
+ 1/1/2014 | Domain name | 2233 | $10.98
+ 1/1/2014 | January Hosting | 2233 | $54.95
+ 1/4/2014 | February Hosting | 2233 | $51.00
+ 1/4/2014 | February Extra Bandwidth | 2233 | $30.00
++----------+--------------------------+-------+---------+
+ TOTAL | $146 93
+ +-------+---------+
+
+```
+
+
+#### Example 3 - CSV
+```go
+table, _ := tablewriter.NewCSV(os.Stdout, "testdata/test_info.csv", true)
+table.SetAlignment(tablewriter.ALIGN_LEFT) // Set Alignment
+table.Render()
+```
+
+##### Output 3
+```
++----------+--------------+------+-----+---------+----------------+
+| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA |
++----------+--------------+------+-----+---------+----------------+
+| user_id | smallint(5) | NO | PRI | NULL | auto_increment |
+| username | varchar(10) | NO | | NULL | |
+| password | varchar(100) | NO | | NULL | |
++----------+--------------+------+-----+---------+----------------+
+```
+
+#### Example 4 - Custom Separator
+```go
+table, _ := tablewriter.NewCSV(os.Stdout, "testdata/test.csv", true)
+table.SetRowLine(true) // Enable row line
+
+// Change table lines
+table.SetCenterSeparator("*")
+table.SetColumnSeparator("╪")
+table.SetRowSeparator("-")
+
+table.SetAlignment(tablewriter.ALIGN_LEFT)
+table.Render()
+```
+
+##### Output 4
+```
+*------------*-----------*---------*
+╪ FIRST NAME ╪ LAST NAME ╪ SSN ╪
+*------------*-----------*---------*
+╪ John ╪ Barry ╪ 123456 ╪
+*------------*-----------*---------*
+╪ Kathy ╪ Smith ╪ 687987 ╪
+*------------*-----------*---------*
+╪ Bob ╪ McCornick ╪ 3979870 ╪
+*------------*-----------*---------*
+```
+
+#### Example 5 - Markdown Format
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "2233", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2233", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "2233", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
+table.SetCenterSeparator("|")
+table.AppendBulk(data) // Add Bulk Data
+table.Render()
+```
+
+##### Output 5
+```
+| DATE | DESCRIPTION | CV2 | AMOUNT |
+|----------|--------------------------|------|--------|
+| 1/1/2014 | Domain name | 2233 | $10.98 |
+| 1/1/2014 | January Hosting | 2233 | $54.95 |
+| 1/4/2014 | February Hosting | 2233 | $51.00 |
+| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |
+```
+
+#### Example 6 - Identical cells merging
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "1234", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2345", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "3456", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetFooter([]string{"", "", "Total", "$146.93"})
+table.SetAutoMergeCells(true)
+table.SetRowLine(true)
+table.AppendBulk(data)
+table.Render()
+```
+
+##### Output 6
+```
++----------+--------------------------+-------+---------+
+| DATE | DESCRIPTION | CV2 | AMOUNT |
++----------+--------------------------+-------+---------+
+| 1/1/2014 | Domain name | 1234 | $10.98 |
++ +--------------------------+-------+---------+
+| | January Hosting | 2345 | $54.95 |
++----------+--------------------------+-------+---------+
+| 1/4/2014 | February Hosting | 3456 | $51.00 |
++ +--------------------------+-------+---------+
+| | February Extra Bandwidth | 4567 | $30.00 |
++----------+--------------------------+-------+---------+
+| TOTAL | $146 93 |
++----------+--------------------------+-------+---------+
+```
+
+
+#### Table with color
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "2233", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2233", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "2233", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
+table.SetBorder(false) // Set Border to false
+
+table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
+ tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
+ tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
+ tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})
+
+table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
+ tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
+ tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
+ tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
+
+table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{},
+ tablewriter.Colors{tablewriter.Bold},
+ tablewriter.Colors{tablewriter.FgHiRedColor})
+
+table.AppendBulk(data)
+table.Render()
+```
+
+#### Table with color Output
+
+
+#### Example 6 - Set table caption
+```go
+data := [][]string{
+ []string{"A", "The Good", "500"},
+ []string{"B", "The Very very Bad Man", "288"},
+ []string{"C", "The Ugly", "120"},
+ []string{"D", "The Gopher", "800"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Name", "Sign", "Rating"})
+table.SetCaption(true, "Movie ratings.")
+
+for _, v := range data {
+ table.Append(v)
+}
+table.Render() // Send output
+```
+
+Note: Caption text will wrap with total width of rendered table.
+
+##### Output 6
+```
++------+-----------------------+--------+
+| NAME | SIGN | RATING |
++------+-----------------------+--------+
+| A | The Good | 500 |
+| B | The Very very Bad Man | 288 |
+| C | The Ugly | 120 |
+| D | The Gopher | 800 |
++------+-----------------------+--------+
+Movie ratings.
+```
+
+#### TODO
+- ~~Import Directly from CSV~~ - `done`
+- ~~Support for `SetFooter`~~ - `done`
+- ~~Support for `SetBorder`~~ - `done`
+- ~~Support table with uneven rows~~ - `done`
+- ~~Support custom alignment~~
+- General Improvement & Optimisation
+- `NewHTML` Parse table from HTML
diff --git a/vendor/github.com/olekukonko/tablewriter/csv.go b/vendor/github.com/olekukonko/tablewriter/csv.go
new file mode 100644
index 0000000..9887830
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/csv.go
@@ -0,0 +1,52 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+package tablewriter
+
+import (
+ "encoding/csv"
+ "io"
+ "os"
+)
+
+// Start A new table by importing from a CSV file
+// Takes io.Writer and csv File name
+func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) {
+ file, err := os.Open(fileName)
+ if err != nil {
+ return &Table{}, err
+ }
+ defer file.Close()
+ csvReader := csv.NewReader(file)
+ t, err := NewCSVReader(writer, csvReader, hasHeader)
+ return t, err
+}
+
+// Start a New Table Writer with csv.Reader
+// This enables customisation such as reader.Comma = ';'
+// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
+func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) {
+ t := NewWriter(writer)
+ if hasHeader {
+ // Read the first row
+ headers, err := csvReader.Read()
+ if err != nil {
+ return &Table{}, err
+ }
+ t.SetHeader(headers)
+ }
+ for {
+ record, err := csvReader.Read()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return &Table{}, err
+ }
+ t.Append(record)
+ }
+ return t, nil
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/table.go b/vendor/github.com/olekukonko/tablewriter/table.go
new file mode 100644
index 0000000..6bbef96
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/table.go
@@ -0,0 +1,839 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+// Create & Generate text based table
+package tablewriter
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "regexp"
+ "strings"
+)
+
+const (
+ MAX_ROW_WIDTH = 30
+)
+
+const (
+ CENTER = "+"
+ ROW = "-"
+ COLUMN = "|"
+ SPACE = " "
+ NEWLINE = "\n"
+)
+
+const (
+ ALIGN_DEFAULT = iota
+ ALIGN_CENTER
+ ALIGN_RIGHT
+ ALIGN_LEFT
+)
+
+var (
+ decimal = regexp.MustCompile(`^-*\d*\.?\d*$`)
+ percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`)
+)
+
+type Border struct {
+ Left bool
+ Right bool
+ Top bool
+ Bottom bool
+}
+
+type Table struct {
+ out io.Writer
+ rows [][]string
+ lines [][][]string
+ cs map[int]int
+ rs map[int]int
+ headers [][]string
+ footers [][]string
+ caption bool
+ captionText string
+ autoFmt bool
+ autoWrap bool
+ reflowText bool
+ mW int
+ pCenter string
+ pRow string
+ pColumn string
+ tColumn int
+ tRow int
+ hAlign int
+ fAlign int
+ align int
+ newLine string
+ rowLine bool
+ autoMergeCells bool
+ hdrLine bool
+ borders Border
+ colSize int
+ headerParams []string
+ columnsParams []string
+ footerParams []string
+ columnsAlign []int
+}
+
+// Start New Table
+// Take io.Writer Directly
+func NewWriter(writer io.Writer) *Table {
+ t := &Table{
+ out: writer,
+ rows: [][]string{},
+ lines: [][][]string{},
+ cs: make(map[int]int),
+ rs: make(map[int]int),
+ headers: [][]string{},
+ footers: [][]string{},
+ caption: false,
+ captionText: "Table caption.",
+ autoFmt: true,
+ autoWrap: true,
+ reflowText: true,
+ mW: MAX_ROW_WIDTH,
+ pCenter: CENTER,
+ pRow: ROW,
+ pColumn: COLUMN,
+ tColumn: -1,
+ tRow: -1,
+ hAlign: ALIGN_DEFAULT,
+ fAlign: ALIGN_DEFAULT,
+ align: ALIGN_DEFAULT,
+ newLine: NEWLINE,
+ rowLine: false,
+ hdrLine: true,
+ borders: Border{Left: true, Right: true, Bottom: true, Top: true},
+ colSize: -1,
+ headerParams: []string{},
+ columnsParams: []string{},
+ footerParams: []string{},
+ columnsAlign: []int{}}
+ return t
+}
+
+// Render table output
+func (t *Table) Render() {
+ if t.borders.Top {
+ t.printLine(true)
+ }
+ t.printHeading()
+ if t.autoMergeCells {
+ t.printRowsMergeCells()
+ } else {
+ t.printRows()
+ }
+ if !t.rowLine && t.borders.Bottom {
+ t.printLine(true)
+ }
+ t.printFooter()
+
+ if t.caption {
+ t.printCaption()
+ }
+}
+
+const (
+ headerRowIdx = -1
+ footerRowIdx = -2
+)
+
+// Set table header
+func (t *Table) SetHeader(keys []string) {
+ t.colSize = len(keys)
+ for i, v := range keys {
+ lines := t.parseDimension(v, i, headerRowIdx)
+ t.headers = append(t.headers, lines)
+ }
+}
+
+// Set table Footer
+func (t *Table) SetFooter(keys []string) {
+ //t.colSize = len(keys)
+ for i, v := range keys {
+ lines := t.parseDimension(v, i, footerRowIdx)
+ t.footers = append(t.footers, lines)
+ }
+}
+
+// Set table Caption
+func (t *Table) SetCaption(caption bool, captionText ...string) {
+ t.caption = caption
+ if len(captionText) == 1 {
+ t.captionText = captionText[0]
+ }
+}
+
+// Turn header autoformatting on/off. Default is on (true).
+func (t *Table) SetAutoFormatHeaders(auto bool) {
+ t.autoFmt = auto
+}
+
+// Turn automatic multiline text adjustment on/off. Default is on (true).
+func (t *Table) SetAutoWrapText(auto bool) {
+ t.autoWrap = auto
+}
+
+// Turn automatic reflowing of multiline text when rewrapping. Default is on (true).
+func (t *Table) SetReflowDuringAutoWrap(auto bool) {
+ t.reflowText = auto
+}
+
+// Set the Default column width
+func (t *Table) SetColWidth(width int) {
+ t.mW = width
+}
+
+// Set the minimal width for a column
+func (t *Table) SetColMinWidth(column int, width int) {
+ t.cs[column] = width
+}
+
+// Set the Column Separator
+func (t *Table) SetColumnSeparator(sep string) {
+ t.pColumn = sep
+}
+
+// Set the Row Separator
+func (t *Table) SetRowSeparator(sep string) {
+ t.pRow = sep
+}
+
+// Set the center Separator
+func (t *Table) SetCenterSeparator(sep string) {
+ t.pCenter = sep
+}
+
+// Set Header Alignment
+func (t *Table) SetHeaderAlignment(hAlign int) {
+ t.hAlign = hAlign
+}
+
+// Set Footer Alignment
+func (t *Table) SetFooterAlignment(fAlign int) {
+ t.fAlign = fAlign
+}
+
+// Set Table Alignment
+func (t *Table) SetAlignment(align int) {
+ t.align = align
+}
+
+func (t *Table) SetColumnAlignment(keys []int) {
+ for _, v := range keys {
+ switch v {
+ case ALIGN_CENTER:
+ break
+ case ALIGN_LEFT:
+ break
+ case ALIGN_RIGHT:
+ break
+ default:
+ v = ALIGN_DEFAULT
+ }
+ t.columnsAlign = append(t.columnsAlign, v)
+ }
+}
+
+// Set New Line
+func (t *Table) SetNewLine(nl string) {
+ t.newLine = nl
+}
+
+// Set Header Line
+// This would enable / disable a line after the header
+func (t *Table) SetHeaderLine(line bool) {
+ t.hdrLine = line
+}
+
+// Set Row Line
+// This would enable / disable a line on each row of the table
+func (t *Table) SetRowLine(line bool) {
+ t.rowLine = line
+}
+
+// Set Auto Merge Cells
+// This would enable / disable the merge of cells with identical values
+func (t *Table) SetAutoMergeCells(auto bool) {
+ t.autoMergeCells = auto
+}
+
+// Set Table Border
+// This would enable / disable line around the table
+func (t *Table) SetBorder(border bool) {
+ t.SetBorders(Border{border, border, border, border})
+}
+
+func (t *Table) SetBorders(border Border) {
+ t.borders = border
+}
+
+// Append row to table
+func (t *Table) Append(row []string) {
+ rowSize := len(t.headers)
+ if rowSize > t.colSize {
+ t.colSize = rowSize
+ }
+
+ n := len(t.lines)
+ line := [][]string{}
+ for i, v := range row {
+
+ // Detect string width
+ // Detect String height
+ // Break strings into words
+ out := t.parseDimension(v, i, n)
+
+ // Append broken words
+ line = append(line, out)
+ }
+ t.lines = append(t.lines, line)
+}
+
+// Allow Support for Bulk Append
+// Eliminates repeated for loops
+func (t *Table) AppendBulk(rows [][]string) {
+ for _, row := range rows {
+ t.Append(row)
+ }
+}
+
+// NumLines to get the number of lines
+func (t *Table) NumLines() int {
+ return len(t.lines)
+}
+
+// Clear rows
+func (t *Table) ClearRows() {
+ t.lines = [][][]string{}
+}
+
+// Clear footer
+func (t *Table) ClearFooter() {
+ t.footers = [][]string{}
+}
+
+// Print line based on row width
+func (t *Table) printLine(nl bool) {
+ fmt.Fprint(t.out, t.pCenter)
+ for i := 0; i < len(t.cs); i++ {
+ v := t.cs[i]
+ fmt.Fprintf(t.out, "%s%s%s%s",
+ t.pRow,
+ strings.Repeat(string(t.pRow), v),
+ t.pRow,
+ t.pCenter)
+ }
+ if nl {
+ fmt.Fprint(t.out, t.newLine)
+ }
+}
+
+// Print line based on row width with our without cell separator
+func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
+ fmt.Fprint(t.out, t.pCenter)
+ for i := 0; i < len(t.cs); i++ {
+ v := t.cs[i]
+ if i > len(displayCellSeparator) || displayCellSeparator[i] {
+ // Display the cell separator
+ fmt.Fprintf(t.out, "%s%s%s%s",
+ t.pRow,
+ strings.Repeat(string(t.pRow), v),
+ t.pRow,
+ t.pCenter)
+ } else {
+ // Don't display the cell separator for this cell
+ fmt.Fprintf(t.out, "%s%s",
+ strings.Repeat(" ", v+2),
+ t.pCenter)
+ }
+ }
+ if nl {
+ fmt.Fprint(t.out, t.newLine)
+ }
+}
+
+// Return the PadRight function if align is left, PadLeft if align is right,
+// and Pad by default
+func pad(align int) func(string, string, int) string {
+ padFunc := Pad
+ switch align {
+ case ALIGN_LEFT:
+ padFunc = PadRight
+ case ALIGN_RIGHT:
+ padFunc = PadLeft
+ }
+ return padFunc
+}
+
+// Print heading information
+func (t *Table) printHeading() {
+ // Check if headers is available
+ if len(t.headers) < 1 {
+ return
+ }
+
+ // Identify last column
+ end := len(t.cs) - 1
+
+ // Get pad function
+ padFunc := pad(t.hAlign)
+
+ // Checking for ANSI escape sequences for header
+ is_esc_seq := false
+ if len(t.headerParams) > 0 {
+ is_esc_seq = true
+ }
+
+ // Maximum height.
+ max := t.rs[headerRowIdx]
+
+ // Print Heading
+ for x := 0; x < max; x++ {
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
+
+ for y := 0; y <= end; y++ {
+ v := t.cs[y]
+ h := ""
+ if y < len(t.headers) && x < len(t.headers[y]) {
+ h = t.headers[y][x]
+ }
+ if t.autoFmt {
+ h = Title(h)
+ }
+ pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn)
+
+ if is_esc_seq {
+ fmt.Fprintf(t.out, " %s %s",
+ format(padFunc(h, SPACE, v),
+ t.headerParams[y]), pad)
+ } else {
+ fmt.Fprintf(t.out, " %s %s",
+ padFunc(h, SPACE, v),
+ pad)
+ }
+ }
+ // Next line
+ fmt.Fprint(t.out, t.newLine)
+ }
+ if t.hdrLine {
+ t.printLine(true)
+ }
+}
+
+// Print heading information
+func (t *Table) printFooter() {
+ // Check if headers is available
+ if len(t.footers) < 1 {
+ return
+ }
+
+ // Only print line if border is not set
+ if !t.borders.Bottom {
+ t.printLine(true)
+ }
+
+ // Identify last column
+ end := len(t.cs) - 1
+
+ // Get pad function
+ padFunc := pad(t.fAlign)
+
+ // Checking for ANSI escape sequences for header
+ is_esc_seq := false
+ if len(t.footerParams) > 0 {
+ is_esc_seq = true
+ }
+
+ // Maximum height.
+ max := t.rs[footerRowIdx]
+
+ // Print Footer
+ erasePad := make([]bool, len(t.footers))
+ for x := 0; x < max; x++ {
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
+
+ for y := 0; y <= end; y++ {
+ v := t.cs[y]
+ f := ""
+ if y < len(t.footers) && x < len(t.footers[y]) {
+ f = t.footers[y][x]
+ }
+ if t.autoFmt {
+ f = Title(f)
+ }
+ pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn)
+
+ if erasePad[y] || (x == 0 && len(f) == 0) {
+ pad = SPACE
+ erasePad[y] = true
+ }
+
+ if is_esc_seq {
+ fmt.Fprintf(t.out, " %s %s",
+ format(padFunc(f, SPACE, v),
+ t.footerParams[y]), pad)
+ } else {
+ fmt.Fprintf(t.out, " %s %s",
+ padFunc(f, SPACE, v),
+ pad)
+ }
+
+ //fmt.Fprintf(t.out, " %s %s",
+ // padFunc(f, SPACE, v),
+ // pad)
+ }
+ // Next line
+ fmt.Fprint(t.out, t.newLine)
+ //t.printLine(true)
+ }
+
+ hasPrinted := false
+
+ for i := 0; i <= end; i++ {
+ v := t.cs[i]
+ pad := t.pRow
+ center := t.pCenter
+ length := len(t.footers[i][0])
+
+ if length > 0 {
+ hasPrinted = true
+ }
+
+ // Set center to be space if length is 0
+ if length == 0 && !t.borders.Right {
+ center = SPACE
+ }
+
+ // Print first junction
+ if i == 0 {
+ fmt.Fprint(t.out, center)
+ }
+
+ // Pad With space of length is 0
+ if length == 0 {
+ pad = SPACE
+ }
+ // Ignore left space of it has printed before
+ if hasPrinted || t.borders.Left {
+ pad = t.pRow
+ center = t.pCenter
+ }
+
+ // Change Center start position
+ if center == SPACE {
+ if i < end && len(t.footers[i+1][0]) != 0 {
+ center = t.pCenter
+ }
+ }
+
+ // Print the footer
+ fmt.Fprintf(t.out, "%s%s%s%s",
+ pad,
+ strings.Repeat(string(pad), v),
+ pad,
+ center)
+
+ }
+
+ fmt.Fprint(t.out, t.newLine)
+}
+
+// Print caption text
+func (t Table) printCaption() {
+ width := t.getTableWidth()
+ paragraph, _ := WrapString(t.captionText, width)
+ for linecount := 0; linecount < len(paragraph); linecount++ {
+ fmt.Fprintln(t.out, paragraph[linecount])
+ }
+}
+
+// Calculate the total number of characters in a row
+func (t Table) getTableWidth() int {
+ var chars int
+ for _, v := range t.cs {
+ chars += v
+ }
+
+ // Add chars, spaces, seperators to calculate the total width of the table.
+ // ncols := t.colSize
+ // spaces := ncols * 2
+ // seps := ncols + 1
+
+ return (chars + (3 * t.colSize) + 2)
+}
+
+func (t Table) printRows() {
+ for i, lines := range t.lines {
+ t.printRow(lines, i)
+ }
+}
+
+func (t *Table) fillAlignment(num int) {
+ if len(t.columnsAlign) < num {
+ t.columnsAlign = make([]int, num)
+ for i := range t.columnsAlign {
+ t.columnsAlign[i] = t.align
+ }
+ }
+}
+
+// Print Row Information
+// Adjust column alignment based on type
+
+func (t *Table) printRow(columns [][]string, rowIdx int) {
+ // Get Maximum Height
+ max := t.rs[rowIdx]
+ total := len(columns)
+
+ // TODO Fix uneven col size
+ // if total < t.colSize {
+ // for n := t.colSize - total; n < t.colSize ; n++ {
+ // columns = append(columns, []string{SPACE})
+ // t.cs[n] = t.mW
+ // }
+ //}
+
+ // Pad Each Height
+ pads := []int{}
+
+ // Checking for ANSI escape sequences for columns
+ is_esc_seq := false
+ if len(t.columnsParams) > 0 {
+ is_esc_seq = true
+ }
+ t.fillAlignment(total)
+
+ for i, line := range columns {
+ length := len(line)
+ pad := max - length
+ pads = append(pads, pad)
+ for n := 0; n < pad; n++ {
+ columns[i] = append(columns[i], " ")
+ }
+ }
+ //fmt.Println(max, "\n")
+ for x := 0; x < max; x++ {
+ for y := 0; y < total; y++ {
+
+ // Check if border is set
+ fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
+
+ fmt.Fprintf(t.out, SPACE)
+ str := columns[y][x]
+
+ // Embedding escape sequence with column value
+ if is_esc_seq {
+ str = format(str, t.columnsParams[y])
+ }
+
+ // This would print alignment
+ // Default alignment would use multiple configuration
+ switch t.columnsAlign[y] {
+ case ALIGN_CENTER: //
+ fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
+ case ALIGN_RIGHT:
+ fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
+ case ALIGN_LEFT:
+ fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
+ default:
+ if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
+ fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
+ } else {
+ fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
+
+ // TODO Custom alignment per column
+ //if max == 1 || pads[y] > 0 {
+ // fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
+ //} else {
+ // fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
+ //}
+
+ }
+ }
+ fmt.Fprintf(t.out, SPACE)
+ }
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
+ fmt.Fprint(t.out, t.newLine)
+ }
+
+ if t.rowLine {
+ t.printLine(true)
+ }
+}
+
+// Print the rows of the table and merge the cells that are identical
+func (t *Table) printRowsMergeCells() {
+ var previousLine []string
+ var displayCellBorder []bool
+ var tmpWriter bytes.Buffer
+ for i, lines := range t.lines {
+ // We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
+ previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
+ if i > 0 { //We don't need to print borders above first line
+ if t.rowLine {
+ t.printLineOptionalCellSeparators(true, displayCellBorder)
+ }
+ }
+ tmpWriter.WriteTo(t.out)
+ }
+ //Print the end of the table
+ if t.rowLine {
+ t.printLine(true)
+ }
+}
+
+// Print Row Information to a writer and merge identical cells.
+// Adjust column alignment based on type
+
+func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) {
+ // Get Maximum Height
+ max := t.rs[rowIdx]
+ total := len(columns)
+
+ // Pad Each Height
+ pads := []int{}
+
+ for i, line := range columns {
+ length := len(line)
+ pad := max - length
+ pads = append(pads, pad)
+ for n := 0; n < pad; n++ {
+ columns[i] = append(columns[i], " ")
+ }
+ }
+
+ var displayCellBorder []bool
+ t.fillAlignment(total)
+ for x := 0; x < max; x++ {
+ for y := 0; y < total; y++ {
+
+ // Check if border is set
+ fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
+
+ fmt.Fprintf(writer, SPACE)
+
+ str := columns[y][x]
+
+ if t.autoMergeCells {
+ //Store the full line to merge mutli-lines cells
+ fullLine := strings.Join(columns[y], " ")
+ if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
+ // If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
+ displayCellBorder = append(displayCellBorder, false)
+ str = ""
+ } else {
+ // First line or different content, keep the content and print the cell border
+ displayCellBorder = append(displayCellBorder, true)
+ }
+ }
+
+ // This would print alignment
+ // Default alignment would use multiple configuration
+ switch t.columnsAlign[y] {
+ case ALIGN_CENTER: //
+ fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
+ case ALIGN_RIGHT:
+ fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
+ case ALIGN_LEFT:
+ fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
+ default:
+ if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
+ fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
+ } else {
+ fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
+ }
+ }
+ fmt.Fprintf(writer, SPACE)
+ }
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
+ fmt.Fprint(writer, t.newLine)
+ }
+
+ //The new previous line is the current one
+ previousLine = make([]string, total)
+ for y := 0; y < total; y++ {
+ previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells
+ }
+ //Returns the newly added line and wether or not a border should be displayed above.
+ return previousLine, displayCellBorder
+}
+
+func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
+ var (
+ raw []string
+ maxWidth int
+ )
+
+ raw = getLines(str)
+ maxWidth = 0
+ for _, line := range raw {
+ if w := DisplayWidth(line); w > maxWidth {
+ maxWidth = w
+ }
+ }
+
+ // If wrapping, ensure that all paragraphs in the cell fit in the
+ // specified width.
+ if t.autoWrap {
+ // If there's a maximum allowed width for wrapping, use that.
+ if maxWidth > t.mW {
+ maxWidth = t.mW
+ }
+
+ // In the process of doing so, we need to recompute maxWidth. This
+ // is because perhaps a word in the cell is longer than the
+ // allowed maximum width in t.mW.
+ newMaxWidth := maxWidth
+ newRaw := make([]string, 0, len(raw))
+
+ if t.reflowText {
+ // Make a single paragraph of everything.
+ raw = []string{strings.Join(raw, " ")}
+ }
+ for i, para := range raw {
+ paraLines, _ := WrapString(para, maxWidth)
+ for _, line := range paraLines {
+ if w := DisplayWidth(line); w > newMaxWidth {
+ newMaxWidth = w
+ }
+ }
+ if i > 0 {
+ newRaw = append(newRaw, " ")
+ }
+ newRaw = append(newRaw, paraLines...)
+ }
+ raw = newRaw
+ maxWidth = newMaxWidth
+ }
+
+ // Store the new known maximum width.
+ v, ok := t.cs[colKey]
+ if !ok || v < maxWidth || v == 0 {
+ t.cs[colKey] = maxWidth
+ }
+
+ // Remember the number of lines for the row printer.
+ h := len(raw)
+ v, ok = t.rs[rowKey]
+
+ if !ok || v < h || v == 0 {
+ t.rs[rowKey] = h
+ }
+ //fmt.Printf("Raw %+v %d\n", raw, len(raw))
+ return raw
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/table_with_color.go b/vendor/github.com/olekukonko/tablewriter/table_with_color.go
new file mode 100644
index 0000000..5a4a53e
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/table_with_color.go
@@ -0,0 +1,134 @@
+package tablewriter
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+const ESC = "\033"
+const SEP = ";"
+
+const (
+ BgBlackColor int = iota + 40
+ BgRedColor
+ BgGreenColor
+ BgYellowColor
+ BgBlueColor
+ BgMagentaColor
+ BgCyanColor
+ BgWhiteColor
+)
+
+const (
+ FgBlackColor int = iota + 30
+ FgRedColor
+ FgGreenColor
+ FgYellowColor
+ FgBlueColor
+ FgMagentaColor
+ FgCyanColor
+ FgWhiteColor
+)
+
+const (
+ BgHiBlackColor int = iota + 100
+ BgHiRedColor
+ BgHiGreenColor
+ BgHiYellowColor
+ BgHiBlueColor
+ BgHiMagentaColor
+ BgHiCyanColor
+ BgHiWhiteColor
+)
+
+const (
+ FgHiBlackColor int = iota + 90
+ FgHiRedColor
+ FgHiGreenColor
+ FgHiYellowColor
+ FgHiBlueColor
+ FgHiMagentaColor
+ FgHiCyanColor
+ FgHiWhiteColor
+)
+
+const (
+ Normal = 0
+ Bold = 1
+ UnderlineSingle = 4
+ Italic
+)
+
+type Colors []int
+
+func startFormat(seq string) string {
+ return fmt.Sprintf("%s[%sm", ESC, seq)
+}
+
+func stopFormat() string {
+ return fmt.Sprintf("%s[%dm", ESC, Normal)
+}
+
+// Making the SGR (Select Graphic Rendition) sequence.
+func makeSequence(codes []int) string {
+ codesInString := []string{}
+ for _, code := range codes {
+ codesInString = append(codesInString, strconv.Itoa(code))
+ }
+ return strings.Join(codesInString, SEP)
+}
+
+// Adding ANSI escape sequences before and after string
+func format(s string, codes interface{}) string {
+ var seq string
+
+ switch v := codes.(type) {
+
+ case string:
+ seq = v
+ case []int:
+ seq = makeSequence(v)
+ default:
+ return s
+ }
+
+ if len(seq) == 0 {
+ return s
+ }
+ return startFormat(seq) + s + stopFormat()
+}
+
+// Adding header colors (ANSI codes)
+func (t *Table) SetHeaderColor(colors ...Colors) {
+ if t.colSize != len(colors) {
+ panic("Number of header colors must be equal to number of headers.")
+ }
+ for i := 0; i < len(colors); i++ {
+ t.headerParams = append(t.headerParams, makeSequence(colors[i]))
+ }
+}
+
+// Adding column colors (ANSI codes)
+func (t *Table) SetColumnColor(colors ...Colors) {
+ if t.colSize != len(colors) {
+ panic("Number of column colors must be equal to number of headers.")
+ }
+ for i := 0; i < len(colors); i++ {
+ t.columnsParams = append(t.columnsParams, makeSequence(colors[i]))
+ }
+}
+
+// Adding column colors (ANSI codes)
+func (t *Table) SetFooterColor(colors ...Colors) {
+ if len(t.footers) != len(colors) {
+ panic("Number of footer colors must be equal to number of footer.")
+ }
+ for i := 0; i < len(colors); i++ {
+ t.footerParams = append(t.footerParams, makeSequence(colors[i]))
+ }
+}
+
+func Color(colors ...int) []int {
+ return colors
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/util.go b/vendor/github.com/olekukonko/tablewriter/util.go
new file mode 100644
index 0000000..9e8f0cb
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/util.go
@@ -0,0 +1,93 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+package tablewriter
+
+import (
+ "math"
+ "regexp"
+ "strings"
+
+ "github.com/mattn/go-runewidth"
+)
+
+var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]")
+
+func DisplayWidth(str string) int {
+ return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, ""))
+}
+
+// Simple Condition for string
+// Returns value based on condition
+func ConditionString(cond bool, valid, inValid string) string {
+ if cond {
+ return valid
+ }
+ return inValid
+}
+
+func isNumOrSpace(r rune) bool {
+ return ('0' <= r && r <= '9') || r == ' '
+}
+
+// Format Table Header
+// Replace _ , . and spaces
+func Title(name string) string {
+ origLen := len(name)
+ rs := []rune(name)
+ for i, r := range rs {
+ switch r {
+ case '_':
+ rs[i] = ' '
+ case '.':
+ // ignore floating number 0.0
+ if (i != 0 && !isNumOrSpace(rs[i-1])) || (i != len(rs)-1 && !isNumOrSpace(rs[i+1])) {
+ rs[i] = ' '
+ }
+ }
+ }
+ name = string(rs)
+ name = strings.TrimSpace(name)
+ if len(name) == 0 && origLen > 0 {
+ // Keep at least one character. This is important to preserve
+ // empty lines in multi-line headers/footers.
+ name = " "
+ }
+ return strings.ToUpper(name)
+}
+
+// Pad String
+// Attempts to play string in the center
+func Pad(s, pad string, width int) string {
+ gap := width - DisplayWidth(s)
+ if gap > 0 {
+ gapLeft := int(math.Ceil(float64(gap / 2)))
+ gapRight := gap - gapLeft
+ return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight)
+ }
+ return s
+}
+
+// Pad String Right position
+// This would pace string at the left side fo the screen
+func PadRight(s, pad string, width int) string {
+ gap := width - DisplayWidth(s)
+ if gap > 0 {
+ return s + strings.Repeat(string(pad), gap)
+ }
+ return s
+}
+
+// Pad String Left position
+// This would pace string at the right side fo the screen
+func PadLeft(s, pad string, width int) string {
+ gap := width - DisplayWidth(s)
+ if gap > 0 {
+ return strings.Repeat(string(pad), gap) + s
+ }
+ return s
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/wrap.go b/vendor/github.com/olekukonko/tablewriter/wrap.go
new file mode 100644
index 0000000..a092ee1
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/wrap.go
@@ -0,0 +1,99 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+package tablewriter
+
+import (
+ "math"
+ "strings"
+
+ "github.com/mattn/go-runewidth"
+)
+
+var (
+ nl = "\n"
+ sp = " "
+)
+
+const defaultPenalty = 1e5
+
+// Wrap wraps s into a paragraph of lines of length lim, with minimal
+// raggedness.
+func WrapString(s string, lim int) ([]string, int) {
+ words := strings.Split(strings.Replace(s, nl, sp, -1), sp)
+ var lines []string
+ max := 0
+ for _, v := range words {
+ max = runewidth.StringWidth(v)
+ if max > lim {
+ lim = max
+ }
+ }
+ for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
+ lines = append(lines, strings.Join(line, sp))
+ }
+ return lines, lim
+}
+
+// WrapWords is the low-level line-breaking algorithm, useful if you need more
+// control over the details of the text wrapping process. For most uses,
+// WrapString will be sufficient and more convenient.
+//
+// WrapWords splits a list of words into lines with minimal "raggedness",
+// treating each rune as one unit, accounting for spc units between adjacent
+// words on each line, and attempting to limit lines to lim units. Raggedness
+// is the total error over all lines, where error is the square of the
+// difference of the length of the line and lim. Too-long lines (which only
+// happen when a single word is longer than lim units) have pen penalty units
+// added to the error.
+func WrapWords(words []string, spc, lim, pen int) [][]string {
+ n := len(words)
+
+ length := make([][]int, n)
+ for i := 0; i < n; i++ {
+ length[i] = make([]int, n)
+ length[i][i] = runewidth.StringWidth(words[i])
+ for j := i + 1; j < n; j++ {
+ length[i][j] = length[i][j-1] + spc + runewidth.StringWidth(words[j])
+ }
+ }
+ nbrk := make([]int, n)
+ cost := make([]int, n)
+ for i := range cost {
+ cost[i] = math.MaxInt32
+ }
+ for i := n - 1; i >= 0; i-- {
+ if length[i][n-1] <= lim {
+ cost[i] = 0
+ nbrk[i] = n
+ } else {
+ for j := i + 1; j < n; j++ {
+ d := lim - length[i][j-1]
+ c := d*d + cost[j]
+ if length[i][j-1] > lim {
+ c += pen // too-long lines get a worse penalty
+ }
+ if c < cost[i] {
+ cost[i] = c
+ nbrk[i] = j
+ }
+ }
+ }
+ }
+ var lines [][]string
+ i := 0
+ for i < n {
+ lines = append(lines, words[i:nbrk[i]])
+ i = nbrk[i]
+ }
+ return lines
+}
+
+// getLines decomposes a multiline string into a slice of strings.
+func getLines(s string) []string {
+ return strings.Split(s, nl)
+}
diff --git a/vendor/github.com/ssor/bom/.travis.yml b/vendor/github.com/ssor/bom/.travis.yml
new file mode 100644
index 0000000..6c7f48e
--- /dev/null
+++ b/vendor/github.com/ssor/bom/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+go:
+ - tip
+ - 1.8
+ - 1.7
+ - 1.6
+ - 1.5
+ - 1.4
+ - 1.3
+ - 1.2
+notifications:
+ email:
+ on_success: change
+ on_failure: always
diff --git a/vendor/github.com/ssor/bom/LICENSE b/vendor/github.com/ssor/bom/LICENSE
new file mode 100644
index 0000000..374f685
--- /dev/null
+++ b/vendor/github.com/ssor/bom/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Asher
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/ssor/bom/README.md b/vendor/github.com/ssor/bom/README.md
new file mode 100644
index 0000000..2dcc289
--- /dev/null
+++ b/vendor/github.com/ssor/bom/README.md
@@ -0,0 +1,23 @@
+# bom
+small tools for cleaning bom from byte array or reader
+
+
+## Installation
+
+```sh
+$ go get github.com/ssor/bom
+```
+
+## How to Use
+
+
+```
+ bs := []byte{bom0, bom1, bom2, 0x11}
+ result := CleanBom(bs)
+```
+
+```
+ bs := []byte{bom0, bom1, bom2, 0x11}
+ result := NewReaderWithoutBom(bytes.NewReader(bs))
+
+```
\ No newline at end of file
diff --git a/vendor/github.com/ssor/bom/bom.go b/vendor/github.com/ssor/bom/bom.go
new file mode 100644
index 0000000..907ea98
--- /dev/null
+++ b/vendor/github.com/ssor/bom/bom.go
@@ -0,0 +1,34 @@
+package bom
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+)
+
+const (
+ bom0 = 0xef
+ bom1 = 0xbb
+ bom2 = 0xbf
+)
+
+// CleanBom retur