Browse Source

add support for command line

fatedier 9 years ago
parent
commit
975c2a97c8

+ 5 - 0
Godeps/Godeps.json

@@ -10,6 +10,11 @@
 			"Comment": "v1.5.0-9-gfb7314f",
 			"Rev": "fb7314f8ac86b83ccd34386518d97cf2363e2ae5"
 		},
+		{
+			"ImportPath": "github.com/docopt/docopt-go",
+			"Comment": "0.6.2",
+			"Rev": "784ddc588536785e7299f7272f39101f7faccc3f"
+		},
 		{
 			"ImportPath": "github.com/vaughan0/go-ini",
 			"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"

+ 25 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore

@@ -0,0 +1,25 @@
+# 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
+
+# coverage droppings
+profile.cov

+ 31 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml

@@ -0,0 +1,31 @@
+# Travis CI (http://travis-ci.org/) is a continuous integration
+# service for open source projects. This file configures it
+# to run unit tests for docopt-go.
+
+language: go
+
+go:
+    - 1.4
+    - 1.5
+    - tip
+
+matrix:
+    fast_finish: true
+
+before_install:
+    - go get golang.org/x/tools/cmd/vet
+    - go get golang.org/x/tools/cmd/cover
+    - go get github.com/golang/lint/golint
+    - go get github.com/mattn/goveralls
+
+install:
+    - go get -d -v ./... && go build -v ./...
+
+script:
+    - go vet -x ./...
+    - $HOME/gopath/bin/golint ./...
+    - go test -v ./...
+    - go test -covermode=count -coverprofile=profile.cov .
+
+after_script:
+    - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci

+ 20 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Keith Batten
+
+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.

+ 88 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/README.md

@@ -0,0 +1,88 @@
+docopt-go
+=========
+
+[![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go)
+[![Coverage Status](https://coveralls.io/repos/docopt/docopt.go/badge.png)](https://coveralls.io/r/docopt/docopt.go)
+[![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.png)](https://godoc.org/github.com/docopt/docopt.go)
+
+An implementation of [docopt](http://docopt.org/) in the
+[Go](http://golang.org/) programming language.
+
+**docopt** helps you create *beautiful* command-line interfaces easily:
+
+```go
+package main
+
+import (
+	"fmt"
+	"github.com/docopt/docopt-go"
+)
+
+func main() {
+	  usage := `Naval Fate.
+
+Usage:
+  naval_fate ship new <name>...
+  naval_fate ship <name> move <x> <y> [--speed=<kn>]
+  naval_fate ship shoot <x> <y>
+  naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
+  naval_fate -h | --help
+  naval_fate --version
+
+Options:
+  -h --help     Show this screen.
+  --version     Show version.
+  --speed=<kn>  Speed in knots [default: 10].
+  --moored      Moored (anchored) mine.
+  --drifting    Drifting mine.`
+
+	  arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false)
+	  fmt.Println(arguments)
+}
+```
+
+**docopt** parses command-line arguments based on a help message. Don't
+write parser code: a good help message already has all the necessary
+information in it.
+
+## Installation
+
+⚠ Use the alias “docopt-go”. To use docopt in your Go code:
+
+```go
+import "github.com/docopt/docopt-go"
+```
+
+To install docopt according to your `$GOPATH`:
+
+```console
+$ go get github.com/docopt/docopt-go
+```
+
+## API
+
+```go
+func Parse(doc string, argv []string, help bool, version string,
+    optionsFirst bool, exit ...bool) (map[string]interface{}, error)
+```
+Parse `argv` based on the command-line interface described in `doc`.
+
+Given a conventional command-line help message, docopt creates a parser and
+processes the arguments. See
+https://github.com/docopt/docopt#help-message-format for a description of the
+help message format. If `argv` is `nil`, `os.Args[1:]` is used.
+
+docopt returns a map of option names to the values parsed from `argv`, and an
+error or `nil`.
+
+More documentation for docopt is available at
+[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go).
+
+## Testing
+
+All tests from the Python version are implemented and passing
+at [Travis CI](https://travis-ci.org/docopt/docopt.go). New
+language-agnostic tests have been added
+to [test_golang.docopt](test_golang.docopt).
+
+To run tests for docopt-go, use `go test`.

+ 1239 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go

@@ -0,0 +1,1239 @@
+// Licensed under terms of MIT license (see LICENSE-MIT)
+// Copyright (c) 2013 Keith Batten, kbatten@gmail.com
+
+/*
+Package docopt parses command-line arguments based on a help message.
+
+⚠ Use the alias “docopt-go”:
+	import "github.com/docopt/docopt-go"
+or
+	$ go get github.com/docopt/docopt-go
+*/
+package docopt
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"regexp"
+	"strings"
+	"unicode"
+)
+
+/*
+Parse `argv` based on the command-line interface described in `doc`.
+
+Given a conventional command-line help message, docopt creates a parser and
+processes the arguments. See
+https://github.com/docopt/docopt#help-message-format for a description of the
+help message format. If `argv` is `nil`, `os.Args[1:]` is used.
+
+docopt returns a map of option names to the values parsed from `argv`, and an
+error or `nil`.
+
+Set `help` to `false` to disable automatic help messages on `-h` or `--help`.
+If `version` is a non-empty string, it will be printed when `--version` is
+specified. Set `optionsFirst` to `true` to require that options always come
+before positional arguments; otherwise they can overlap.
+
+By default, docopt calls `os.Exit(0)` if it handled a built-in option such as
+`-h` or `--version`. If the user errored with a wrong command or options,
+docopt exits with a return code of 1. To stop docopt from calling `os.Exit()`
+and to handle your own return codes, pass an optional last parameter of `false`
+for `exit`.
+*/
+func Parse(doc string, argv []string, help bool, version string,
+	optionsFirst bool, exit ...bool) (map[string]interface{}, error) {
+	// if "false" was the (optional) last arg, don't call os.Exit()
+	exitOk := true
+	if len(exit) > 0 {
+		exitOk = exit[0]
+	}
+	args, output, err := parse(doc, argv, help, version, optionsFirst)
+	if _, ok := err.(*UserError); ok {
+		// the user gave us bad input
+		fmt.Fprintln(os.Stderr, output)
+		if exitOk {
+			os.Exit(1)
+		}
+	} else if len(output) > 0 && err == nil {
+		// the user asked for help or `--version`
+		fmt.Println(output)
+		if exitOk {
+			os.Exit(0)
+		}
+	}
+	return args, err
+}
+
+// parse and return a map of args, output and all errors
+func parse(doc string, argv []string, help bool, version string, optionsFirst bool) (args map[string]interface{}, output string, err error) {
+	if argv == nil && len(os.Args) > 1 {
+		argv = os.Args[1:]
+	}
+
+	usageSections := parseSection("usage:", doc)
+
+	if len(usageSections) == 0 {
+		err = newLanguageError("\"usage:\" (case-insensitive) not found.")
+		return
+	}
+	if len(usageSections) > 1 {
+		err = newLanguageError("More than one \"usage:\" (case-insensitive).")
+		return
+	}
+	usage := usageSections[0]
+
+	options := parseDefaults(doc)
+	formal, err := formalUsage(usage)
+	if err != nil {
+		output = handleError(err, usage)
+		return
+	}
+
+	pat, err := parsePattern(formal, &options)
+	if err != nil {
+		output = handleError(err, usage)
+		return
+	}
+
+	patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst)
+	if err != nil {
+		output = handleError(err, usage)
+		return
+	}
+	patFlat, err := pat.flat(patternOption)
+	if err != nil {
+		output = handleError(err, usage)
+		return
+	}
+	patternOptions := patFlat.unique()
+
+	patFlat, err = pat.flat(patternOptionSSHORTCUT)
+	if err != nil {
+		output = handleError(err, usage)
+		return
+	}
+	for _, optionsShortcut := range patFlat {
+		docOptions := parseDefaults(doc)
+		optionsShortcut.children = docOptions.unique().diff(patternOptions)
+	}
+
+	if output = extras(help, version, patternArgv, doc); len(output) > 0 {
+		return
+	}
+
+	err = pat.fix()
+	if err != nil {
+		output = handleError(err, usage)
+		return
+	}
+	matched, left, collected := pat.match(&patternArgv, nil)
+	if matched && len(*left) == 0 {
+		patFlat, err = pat.flat(patternDefault)
+		if err != nil {
+			output = handleError(err, usage)
+			return
+		}
+		args = append(patFlat, *collected...).dictionary()
+		return
+	}
+
+	err = newUserError("")
+	output = handleError(err, usage)
+	return
+}
+
+func handleError(err error, usage string) string {
+	if _, ok := err.(*UserError); ok {
+		return strings.TrimSpace(fmt.Sprintf("%s\n%s", err, usage))
+	}
+	return ""
+}
+
+func parseSection(name, source string) []string {
+	p := regexp.MustCompile(`(?im)^([^\n]*` + name + `[^\n]*\n?(?:[ \t].*?(?:\n|$))*)`)
+	s := p.FindAllString(source, -1)
+	if s == nil {
+		s = []string{}
+	}
+	for i, v := range s {
+		s[i] = strings.TrimSpace(v)
+	}
+	return s
+}
+
+func parseDefaults(doc string) patternList {
+	defaults := patternList{}
+	p := regexp.MustCompile(`\n[ \t]*(-\S+?)`)
+	for _, s := range parseSection("options:", doc) {
+		// FIXME corner case "bla: options: --foo"
+		_, _, s = stringPartition(s, ":") // get rid of "options:"
+		split := p.Split("\n"+s, -1)[1:]
+		match := p.FindAllStringSubmatch("\n"+s, -1)
+		for i := range split {
+			optionDescription := match[i][1] + split[i]
+			if strings.HasPrefix(optionDescription, "-") {
+				defaults = append(defaults, parseOption(optionDescription))
+			}
+		}
+	}
+	return defaults
+}
+
+func parsePattern(source string, options *patternList) (*pattern, error) {
+	tokens := tokenListFromPattern(source)
+	result, err := parseExpr(tokens, options)
+	if err != nil {
+		return nil, err
+	}
+	if tokens.current() != nil {
+		return nil, tokens.errorFunc("unexpected ending: %s" + strings.Join(tokens.tokens, " "))
+	}
+	return newRequired(result...), nil
+}
+
+func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool) (patternList, error) {
+	/*
+		Parse command-line argument vector.
+
+		If options_first:
+			argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
+		else:
+			argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
+	*/
+	parsed := patternList{}
+	for tokens.current() != nil {
+		if tokens.current().eq("--") {
+			for _, v := range tokens.tokens {
+				parsed = append(parsed, newArgument("", v))
+			}
+			return parsed, nil
+		} else if tokens.current().hasPrefix("--") {
+			pl, err := parseLong(tokens, options)
+			if err != nil {
+				return nil, err
+			}
+			parsed = append(parsed, pl...)
+		} else if tokens.current().hasPrefix("-") && !tokens.current().eq("-") {
+			ps, err := parseShorts(tokens, options)
+			if err != nil {
+				return nil, err
+			}
+			parsed = append(parsed, ps...)
+		} else if optionsFirst {
+			for _, v := range tokens.tokens {
+				parsed = append(parsed, newArgument("", v))
+			}
+			return parsed, nil
+		} else {
+			parsed = append(parsed, newArgument("", tokens.move().String()))
+		}
+	}
+	return parsed, nil
+}
+
+func parseOption(optionDescription string) *pattern {
+	optionDescription = strings.TrimSpace(optionDescription)
+	options, _, description := stringPartition(optionDescription, "  ")
+	options = strings.Replace(options, ",", " ", -1)
+	options = strings.Replace(options, "=", " ", -1)
+
+	short := ""
+	long := ""
+	argcount := 0
+	var value interface{}
+	value = false
+
+	reDefault := regexp.MustCompile(`(?i)\[default: (.*)\]`)
+	for _, s := range strings.Fields(options) {
+		if strings.HasPrefix(s, "--") {
+			long = s
+		} else if strings.HasPrefix(s, "-") {
+			short = s
+		} else {
+			argcount = 1
+		}
+		if argcount > 0 {
+			matched := reDefault.FindAllStringSubmatch(description, -1)
+			if len(matched) > 0 {
+				value = matched[0][1]
+			} else {
+				value = nil
+			}
+		}
+	}
+	return newOption(short, long, argcount, value)
+}
+
+func parseExpr(tokens *tokenList, options *patternList) (patternList, error) {
+	// expr ::= seq ( '|' seq )* ;
+	seq, err := parseSeq(tokens, options)
+	if err != nil {
+		return nil, err
+	}
+	if !tokens.current().eq("|") {
+		return seq, nil
+	}
+	var result patternList
+	if len(seq) > 1 {
+		result = patternList{newRequired(seq...)}
+	} else {
+		result = seq
+	}
+	for tokens.current().eq("|") {
+		tokens.move()
+		seq, err = parseSeq(tokens, options)
+		if err != nil {
+			return nil, err
+		}
+		if len(seq) > 1 {
+			result = append(result, newRequired(seq...))
+		} else {
+			result = append(result, seq...)
+		}
+	}
+	if len(result) > 1 {
+		return patternList{newEither(result...)}, nil
+	}
+	return result, nil
+}
+
+func parseSeq(tokens *tokenList, options *patternList) (patternList, error) {
+	// seq ::= ( atom [ '...' ] )* ;
+	result := patternList{}
+	for !tokens.current().match(true, "]", ")", "|") {
+		atom, err := parseAtom(tokens, options)
+		if err != nil {
+			return nil, err
+		}
+		if tokens.current().eq("...") {
+			atom = patternList{newOneOrMore(atom...)}
+			tokens.move()
+		}
+		result = append(result, atom...)
+	}
+	return result, nil
+}
+
+func parseAtom(tokens *tokenList, options *patternList) (patternList, error) {
+	// atom ::= '(' expr ')' | '[' expr ']' | 'options' | long | shorts | argument | command ;
+	tok := tokens.current()
+	result := patternList{}
+	if tokens.current().match(false, "(", "[") {
+		tokens.move()
+		var matching string
+		pl, err := parseExpr(tokens, options)
+		if err != nil {
+			return nil, err
+		}
+		if tok.eq("(") {
+			matching = ")"
+			result = patternList{newRequired(pl...)}
+		} else if tok.eq("[") {
+			matching = "]"
+			result = patternList{newOptional(pl...)}
+		}
+		moved := tokens.move()
+		if !moved.eq(matching) {
+			return nil, tokens.errorFunc("unmatched '%s', expected: '%s' got: '%s'", tok, matching, moved)
+		}
+		return result, nil
+	} else if tok.eq("options") {
+		tokens.move()
+		return patternList{newOptionsShortcut()}, nil
+	} else if tok.hasPrefix("--") && !tok.eq("--") {
+		return parseLong(tokens, options)
+	} else if tok.hasPrefix("-") && !tok.eq("-") && !tok.eq("--") {
+		return parseShorts(tokens, options)
+	} else if tok.hasPrefix("<") && tok.hasSuffix(">") || tok.isUpper() {
+		return patternList{newArgument(tokens.move().String(), nil)}, nil
+	}
+	return patternList{newCommand(tokens.move().String(), false)}, nil
+}
+
+func parseLong(tokens *tokenList, options *patternList) (patternList, error) {
+	// long ::= '--' chars [ ( ' ' | '=' ) chars ] ;
+	long, eq, v := stringPartition(tokens.move().String(), "=")
+	var value interface{}
+	var opt *pattern
+	if eq == "" && v == "" {
+		value = nil
+	} else {
+		value = v
+	}
+
+	if !strings.HasPrefix(long, "--") {
+		return nil, newError("long option '%s' doesn't start with --", long)
+	}
+	similar := patternList{}
+	for _, o := range *options {
+		if o.long == long {
+			similar = append(similar, o)
+		}
+	}
+	if tokens.err == errorUser && len(similar) == 0 { // if no exact match
+		similar = patternList{}
+		for _, o := range *options {
+			if strings.HasPrefix(o.long, long) {
+				similar = append(similar, o)
+			}
+		}
+	}
+	if len(similar) > 1 { // might be simply specified ambiguously 2+ times?
+		similarLong := make([]string, len(similar))
+		for i, s := range similar {
+			similarLong[i] = s.long
+		}
+		return nil, tokens.errorFunc("%s is not a unique prefix: %s?", long, strings.Join(similarLong, ", "))
+	} else if len(similar) < 1 {
+		argcount := 0
+		if eq == "=" {
+			argcount = 1
+		}
+		opt = newOption("", long, argcount, false)
+		*options = append(*options, opt)
+		if tokens.err == errorUser {
+			var val interface{}
+			if argcount > 0 {
+				val = value
+			} else {
+				val = true
+			}
+			opt = newOption("", long, argcount, val)
+		}
+	} else {
+		opt = newOption(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value)
+		if opt.argcount == 0 {
+			if value != nil {
+				return nil, tokens.errorFunc("%s must not have an argument", opt.long)
+			}
+		} else {
+			if value == nil {
+				if tokens.current().match(true, "--") {
+					return nil, tokens.errorFunc("%s requires argument", opt.long)
+				}
+				moved := tokens.move()
+				if moved != nil {
+					value = moved.String() // only set as string if not nil
+				}
+			}
+		}
+		if tokens.err == errorUser {
+			if value != nil {
+				opt.value = value
+			} else {
+				opt.value = true
+			}
+		}
+	}
+
+	return patternList{opt}, nil
+}
+
+func parseShorts(tokens *tokenList, options *patternList) (patternList, error) {
+	// shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;
+	tok := tokens.move()
+	if !tok.hasPrefix("-") || tok.hasPrefix("--") {
+		return nil, newError("short option '%s' doesn't start with -", tok)
+	}
+	left := strings.TrimLeft(tok.String(), "-")
+	parsed := patternList{}
+	for left != "" {
+		var opt *pattern
+		short := "-" + left[0:1]
+		left = left[1:]
+		similar := patternList{}
+		for _, o := range *options {
+			if o.short == short {
+				similar = append(similar, o)
+			}
+		}
+		if len(similar) > 1 {
+			return nil, tokens.errorFunc("%s is specified ambiguously %d times", short, len(similar))
+		} else if len(similar) < 1 {
+			opt = newOption(short, "", 0, false)
+			*options = append(*options, opt)
+			if tokens.err == errorUser {
+				opt = newOption(short, "", 0, true)
+			}
+		} else { // why copying is necessary here?
+			opt = newOption(short, similar[0].long, similar[0].argcount, similar[0].value)
+			var value interface{}
+			if opt.argcount > 0 {
+				if left == "" {
+					if tokens.current().match(true, "--") {
+						return nil, tokens.errorFunc("%s requires argument", short)
+					}
+					value = tokens.move().String()
+				} else {
+					value = left
+					left = ""
+				}
+			}
+			if tokens.err == errorUser {
+				if value != nil {
+					opt.value = value
+				} else {
+					opt.value = true
+				}
+			}
+		}
+		parsed = append(parsed, opt)
+	}
+	return parsed, nil
+}
+
+func newTokenList(source []string, err errorType) *tokenList {
+	errorFunc := newError
+	if err == errorUser {
+		errorFunc = newUserError
+	} else if err == errorLanguage {
+		errorFunc = newLanguageError
+	}
+	return &tokenList{source, errorFunc, err}
+}
+
+func tokenListFromString(source string) *tokenList {
+	return newTokenList(strings.Fields(source), errorUser)
+}
+
+func tokenListFromPattern(source string) *tokenList {
+	p := regexp.MustCompile(`([\[\]\(\)\|]|\.\.\.)`)
+	source = p.ReplaceAllString(source, ` $1 `)
+	p = regexp.MustCompile(`\s+|(\S*<.*?>)`)
+	split := p.Split(source, -1)
+	match := p.FindAllStringSubmatch(source, -1)
+	var result []string
+	l := len(split)
+	for i := 0; i < l; i++ {
+		if len(split[i]) > 0 {
+			result = append(result, split[i])
+		}
+		if i < l-1 && len(match[i][1]) > 0 {
+			result = append(result, match[i][1])
+		}
+	}
+	return newTokenList(result, errorLanguage)
+}
+
+func formalUsage(section string) (string, error) {
+	_, _, section = stringPartition(section, ":") // drop "usage:"
+	pu := strings.Fields(section)
+
+	if len(pu) == 0 {
+		return "", newLanguageError("no fields found in usage (perhaps a spacing error).")
+	}
+
+	result := "( "
+	for _, s := range pu[1:] {
+		if s == pu[0] {
+			result += ") | ( "
+		} else {
+			result += s + " "
+		}
+	}
+	result += ")"
+
+	return result, nil
+}
+
+func extras(help bool, version string, options patternList, doc string) string {
+	if help {
+		for _, o := range options {
+			if (o.name == "-h" || o.name == "--help") && o.value == true {
+				return strings.Trim(doc, "\n")
+			}
+		}
+	}
+	if version != "" {
+		for _, o := range options {
+			if (o.name == "--version") && o.value == true {
+				return version
+			}
+		}
+	}
+	return ""
+}
+
+type errorType int
+
+const (
+	errorUser errorType = iota
+	errorLanguage
+)
+
+func (e errorType) String() string {
+	switch e {
+	case errorUser:
+		return "errorUser"
+	case errorLanguage:
+		return "errorLanguage"
+	}
+	return ""
+}
+
+// UserError records an error with program arguments.
+type UserError struct {
+	msg   string
+	Usage string
+}
+
+func (e UserError) Error() string {
+	return e.msg
+}
+func newUserError(msg string, f ...interface{}) error {
+	return &UserError{fmt.Sprintf(msg, f...), ""}
+}
+
+// LanguageError records an error with the doc string.
+type LanguageError struct {
+	msg string
+}
+
+func (e LanguageError) Error() string {
+	return e.msg
+}
+func newLanguageError(msg string, f ...interface{}) error {
+	return &LanguageError{fmt.Sprintf(msg, f...)}
+}
+
+var newError = fmt.Errorf
+
+type tokenList struct {
+	tokens    []string
+	errorFunc func(string, ...interface{}) error
+	err       errorType
+}
+type token string
+
+func (t *token) eq(s string) bool {
+	if t == nil {
+		return false
+	}
+	return string(*t) == s
+}
+func (t *token) match(matchNil bool, tokenStrings ...string) bool {
+	if t == nil && matchNil {
+		return true
+	} else if t == nil && !matchNil {
+		return false
+	}
+
+	for _, tok := range tokenStrings {
+		if tok == string(*t) {
+			return true
+		}
+	}
+	return false
+}
+func (t *token) hasPrefix(prefix string) bool {
+	if t == nil {
+		return false
+	}
+	return strings.HasPrefix(string(*t), prefix)
+}
+func (t *token) hasSuffix(suffix string) bool {
+	if t == nil {
+		return false
+	}
+	return strings.HasSuffix(string(*t), suffix)
+}
+func (t *token) isUpper() bool {
+	if t == nil {
+		return false
+	}
+	return isStringUppercase(string(*t))
+}
+func (t *token) String() string {
+	if t == nil {
+		return ""
+	}
+	return string(*t)
+}
+
+func (tl *tokenList) current() *token {
+	if len(tl.tokens) > 0 {
+		return (*token)(&(tl.tokens[0]))
+	}
+	return nil
+}
+
+func (tl *tokenList) length() int {
+	return len(tl.tokens)
+}
+
+func (tl *tokenList) move() *token {
+	if len(tl.tokens) > 0 {
+		t := tl.tokens[0]
+		tl.tokens = tl.tokens[1:]
+		return (*token)(&t)
+	}
+	return nil
+}
+
+type patternType uint
+
+const (
+	// leaf
+	patternArgument patternType = 1 << iota
+	patternCommand
+	patternOption
+
+	// branch
+	patternRequired
+	patternOptionAL
+	patternOptionSSHORTCUT // Marker/placeholder for [options] shortcut.
+	patternOneOrMore
+	patternEither
+
+	patternLeaf = patternArgument +
+		patternCommand +
+		patternOption
+	patternBranch = patternRequired +
+		patternOptionAL +
+		patternOptionSSHORTCUT +
+		patternOneOrMore +
+		patternEither
+	patternAll     = patternLeaf + patternBranch
+	patternDefault = 0
+)
+
+func (pt patternType) String() string {
+	switch pt {
+	case patternArgument:
+		return "argument"
+	case patternCommand:
+		return "command"
+	case patternOption:
+		return "option"
+	case patternRequired:
+		return "required"
+	case patternOptionAL:
+		return "optional"
+	case patternOptionSSHORTCUT:
+		return "optionsshortcut"
+	case patternOneOrMore:
+		return "oneormore"
+	case patternEither:
+		return "either"
+	case patternLeaf:
+		return "leaf"
+	case patternBranch:
+		return "branch"
+	case patternAll:
+		return "all"
+	case patternDefault:
+		return "default"
+	}
+	return ""
+}
+
+type pattern struct {
+	t patternType
+
+	children patternList
+
+	name  string
+	value interface{}
+
+	short    string
+	long     string
+	argcount int
+}
+
+type patternList []*pattern
+
+func newBranchPattern(t patternType, pl ...*pattern) *pattern {
+	var p pattern
+	p.t = t
+	p.children = make(patternList, len(pl))
+	copy(p.children, pl)
+	return &p
+}
+
+func newRequired(pl ...*pattern) *pattern {
+	return newBranchPattern(patternRequired, pl...)
+}
+
+func newEither(pl ...*pattern) *pattern {
+	return newBranchPattern(patternEither, pl...)
+}
+
+func newOneOrMore(pl ...*pattern) *pattern {
+	return newBranchPattern(patternOneOrMore, pl...)
+}
+
+func newOptional(pl ...*pattern) *pattern {
+	return newBranchPattern(patternOptionAL, pl...)
+}
+
+func newOptionsShortcut() *pattern {
+	var p pattern
+	p.t = patternOptionSSHORTCUT
+	return &p
+}
+
+func newLeafPattern(t patternType, name string, value interface{}) *pattern {
+	// default: value=nil
+	var p pattern
+	p.t = t
+	p.name = name
+	p.value = value
+	return &p
+}
+
+func newArgument(name string, value interface{}) *pattern {
+	// default: value=nil
+	return newLeafPattern(patternArgument, name, value)
+}
+
+func newCommand(name string, value interface{}) *pattern {
+	// default: value=false
+	var p pattern
+	p.t = patternCommand
+	p.name = name
+	p.value = value
+	return &p
+}
+
+func newOption(short, long string, argcount int, value interface{}) *pattern {
+	// default: "", "", 0, false
+	var p pattern
+	p.t = patternOption
+	p.short = short
+	p.long = long
+	if long != "" {
+		p.name = long
+	} else {
+		p.name = short
+	}
+	p.argcount = argcount
+	if value == false && argcount > 0 {
+		p.value = nil
+	} else {
+		p.value = value
+	}
+	return &p
+}
+
+func (p *pattern) flat(types patternType) (patternList, error) {
+	if p.t&patternLeaf != 0 {
+		if types == patternDefault {
+			types = patternAll
+		}
+		if p.t&types != 0 {
+			return patternList{p}, nil
+		}
+		return patternList{}, nil
+	}
+
+	if p.t&patternBranch != 0 {
+		if p.t&types != 0 {
+			return patternList{p}, nil
+		}
+		result := patternList{}
+		for _, child := range p.children {
+			childFlat, err := child.flat(types)
+			if err != nil {
+				return nil, err
+			}
+			result = append(result, childFlat...)
+		}
+		return result, nil
+	}
+	return nil, newError("unknown pattern type: %d, %d", p.t, types)
+}
+
+func (p *pattern) fix() error {
+	err := p.fixIdentities(nil)
+	if err != nil {
+		return err
+	}
+	p.fixRepeatingArguments()
+	return nil
+}
+
+func (p *pattern) fixIdentities(uniq patternList) error {
+	// Make pattern-tree tips point to same object if they are equal.
+	if p.t&patternBranch == 0 {
+		return nil
+	}
+	if uniq == nil {
+		pFlat, err := p.flat(patternDefault)
+		if err != nil {
+			return err
+		}
+		uniq = pFlat.unique()
+	}
+	for i, child := range p.children {
+		if child.t&patternBranch == 0 {
+			ind, err := uniq.index(child)
+			if err != nil {
+				return err
+			}
+			p.children[i] = uniq[ind]
+		} else {
+			err := child.fixIdentities(uniq)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (p *pattern) fixRepeatingArguments() {
+	// Fix elements that should accumulate/increment values.
+	var either []patternList
+
+	for _, child := range p.transform().children {
+		either = append(either, child.children)
+	}
+	for _, cas := range either {
+		casMultiple := patternList{}
+		for _, e := range cas {
+			if cas.count(e) > 1 {
+				casMultiple = append(casMultiple, e)
+			}
+		}
+		for _, e := range casMultiple {
+			if e.t == patternArgument || e.t == patternOption && e.argcount > 0 {
+				switch e.value.(type) {
+				case string:
+					e.value = strings.Fields(e.value.(string))
+				case []string:
+				default:
+					e.value = []string{}
+				}
+			}
+			if e.t == patternCommand || e.t == patternOption && e.argcount == 0 {
+				e.value = 0
+			}
+		}
+	}
+}
+
+func (p *pattern) match(left *patternList, collected *patternList) (bool, *patternList, *patternList) {
+	if collected == nil {
+		collected = &patternList{}
+	}
+	if p.t&patternRequired != 0 {
+		l := left
+		c := collected
+		for _, p := range p.children {
+			var matched bool
+			matched, l, c = p.match(l, c)
+			if !matched {
+				return false, left, collected
+			}
+		}
+		return true, l, c
+	} else if p.t&patternOptionAL != 0 || p.t&patternOptionSSHORTCUT != 0 {
+		for _, p := range p.children {
+			_, left, collected = p.match(left, collected)
+		}
+		return true, left, collected
+	} else if p.t&patternOneOrMore != 0 {
+		if len(p.children) != 1 {
+			panic("OneOrMore.match(): assert len(p.children) == 1")
+		}
+		l := left
+		c := collected
+		var lAlt *patternList
+		matched := true
+		times := 0
+		for matched {
+			// could it be that something didn't match but changed l or c?
+			matched, l, c = p.children[0].match(l, c)
+			if matched {
+				times++
+			}
+			if lAlt == l {
+				break
+			}
+			lAlt = l
+		}
+		if times >= 1 {
+			return true, l, c
+		}
+		return false, left, collected
+	} else if p.t&patternEither != 0 {
+		type outcomeStruct struct {
+			matched   bool
+			left      *patternList
+			collected *patternList
+			length    int
+		}
+		outcomes := []outcomeStruct{}
+		for _, p := range p.children {
+			matched, l, c := p.match(left, collected)
+			outcome := outcomeStruct{matched, l, c, len(*l)}
+			if matched {
+				outcomes = append(outcomes, outcome)
+			}
+		}
+		if len(outcomes) > 0 {
+			minLen := outcomes[0].length
+			minIndex := 0
+			for i, v := range outcomes {
+				if v.length < minLen {
+					minIndex = i
+				}
+			}
+			return outcomes[minIndex].matched, outcomes[minIndex].left, outcomes[minIndex].collected
+		}
+		return false, left, collected
+	} else if p.t&patternLeaf != 0 {
+		pos, match := p.singleMatch(left)
+		var increment interface{}
+		if match == nil {
+			return false, left, collected
+		}
+		leftAlt := make(patternList, len((*left)[:pos]), len((*left)[:pos])+len((*left)[pos+1:]))
+		copy(leftAlt, (*left)[:pos])
+		leftAlt = append(leftAlt, (*left)[pos+1:]...)
+		sameName := patternList{}
+		for _, a := range *collected {
+			if a.name == p.name {
+				sameName = append(sameName, a)
+			}
+		}
+
+		switch p.value.(type) {
+		case int, []string:
+			switch p.value.(type) {
+			case int:
+				increment = 1
+			case []string:
+				switch match.value.(type) {
+				case string:
+					increment = []string{match.value.(string)}
+				default:
+					increment = match.value
+				}
+			}
+			if len(sameName) == 0 {
+				match.value = increment
+				collectedMatch := make(patternList, len(*collected), len(*collected)+1)
+				copy(collectedMatch, *collected)
+				collectedMatch = append(collectedMatch, match)
+				return true, &leftAlt, &collectedMatch
+			}
+			switch sameName[0].value.(type) {
+			case int:
+				sameName[0].value = sameName[0].value.(int) + increment.(int)
+			case []string:
+				sameName[0].value = append(sameName[0].value.([]string), increment.([]string)...)
+			}
+			return true, &leftAlt, collected
+		}
+		collectedMatch := make(patternList, len(*collected), len(*collected)+1)
+		copy(collectedMatch, *collected)
+		collectedMatch = append(collectedMatch, match)
+		return true, &leftAlt, &collectedMatch
+	}
+	panic("unmatched type")
+}
+
+func (p *pattern) singleMatch(left *patternList) (int, *pattern) {
+	if p.t&patternArgument != 0 {
+		for n, pat := range *left {
+			if pat.t&patternArgument != 0 {
+				return n, newArgument(p.name, pat.value)
+			}
+		}
+		return -1, nil
+	} else if p.t&patternCommand != 0 {
+		for n, pat := range *left {
+			if pat.t&patternArgument != 0 {
+				if pat.value == p.name {
+					return n, newCommand(p.name, true)
+				}
+				break
+			}
+		}
+		return -1, nil
+	} else if p.t&patternOption != 0 {
+		for n, pat := range *left {
+			if p.name == pat.name {
+				return n, pat
+			}
+		}
+		return -1, nil
+	}
+	panic("unmatched type")
+}
+
+func (p *pattern) String() string {
+	if p.t&patternOption != 0 {
+		return fmt.Sprintf("%s(%s, %s, %d, %+v)", p.t, p.short, p.long, p.argcount, p.value)
+	} else if p.t&patternLeaf != 0 {
+		return fmt.Sprintf("%s(%s, %+v)", p.t, p.name, p.value)
+	} else if p.t&patternBranch != 0 {
+		result := ""
+		for i, child := range p.children {
+			if i > 0 {
+				result += ", "
+			}
+			result += child.String()
+		}
+		return fmt.Sprintf("%s(%s)", p.t, result)
+	}
+	panic("unmatched type")
+}
+
+func (p *pattern) transform() *pattern {
+	/*
+		Expand pattern into an (almost) equivalent one, but with single Either.
+
+		Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d)
+		Quirks: [-a] => (-a), (-a...) => (-a -a)
+	*/
+	result := []patternList{}
+	groups := []patternList{patternList{p}}
+	parents := patternRequired +
+		patternOptionAL +
+		patternOptionSSHORTCUT +
+		patternEither +
+		patternOneOrMore
+	for len(groups) > 0 {
+		children := groups[0]
+		groups = groups[1:]
+		var child *pattern
+		for _, c := range children {
+			if c.t&parents != 0 {
+				child = c
+				break
+			}
+		}
+		if child != nil {
+			children.remove(child)
+			if child.t&patternEither != 0 {
+				for _, c := range child.children {
+					r := patternList{}
+					r = append(r, c)
+					r = append(r, children...)
+					groups = append(groups, r)
+				}
+			} else if child.t&patternOneOrMore != 0 {
+				r := patternList{}
+				r = append(r, child.children.double()...)
+				r = append(r, children...)
+				groups = append(groups, r)
+			} else {
+				r := patternList{}
+				r = append(r, child.children...)
+				r = append(r, children...)
+				groups = append(groups, r)
+			}
+		} else {
+			result = append(result, children)
+		}
+	}
+	either := patternList{}
+	for _, e := range result {
+		either = append(either, newRequired(e...))
+	}
+	return newEither(either...)
+}
+
+func (p *pattern) eq(other *pattern) bool {
+	return reflect.DeepEqual(p, other)
+}
+
+func (pl patternList) unique() patternList {
+	table := make(map[string]bool)
+	result := patternList{}
+	for _, v := range pl {
+		if !table[v.String()] {
+			table[v.String()] = true
+			result = append(result, v)
+		}
+	}
+	return result
+}
+
+func (pl patternList) index(p *pattern) (int, error) {
+	for i, c := range pl {
+		if c.eq(p) {
+			return i, nil
+		}
+	}
+	return -1, newError("%s not in list", p)
+}
+
+func (pl patternList) count(p *pattern) int {
+	count := 0
+	for _, c := range pl {
+		if c.eq(p) {
+			count++
+		}
+	}
+	return count
+}
+
+func (pl patternList) diff(l patternList) patternList {
+	lAlt := make(patternList, len(l))
+	copy(lAlt, l)
+	result := make(patternList, 0, len(pl))
+	for _, v := range pl {
+		if v != nil {
+			match := false
+			for i, w := range lAlt {
+				if w.eq(v) {
+					match = true
+					lAlt[i] = nil
+					break
+				}
+			}
+			if match == false {
+				result = append(result, v)
+			}
+		}
+	}
+	return result
+}
+
+func (pl patternList) double() patternList {
+	l := len(pl)
+	result := make(patternList, l*2)
+	copy(result, pl)
+	copy(result[l:2*l], pl)
+	return result
+}
+
+func (pl *patternList) remove(p *pattern) {
+	(*pl) = pl.diff(patternList{p})
+}
+
+func (pl patternList) dictionary() map[string]interface{} {
+	dict := make(map[string]interface{})
+	for _, a := range pl {
+		dict[a.name] = a.value
+	}
+	return dict
+}
+
+func stringPartition(s, sep string) (string, string, string) {
+	sepPos := strings.Index(s, sep)
+	if sepPos == -1 { // no seperator found
+		return s, "", ""
+	}
+	split := strings.SplitN(s, sep, 2)
+	return split[0], sep, split[1]
+}
+
+// returns true if all cased characters in the string are uppercase
+// and there are there is at least one cased charcter
+func isStringUppercase(s string) bool {
+	if strings.ToUpper(s) != s {
+		return false
+	}
+	for _, c := range []rune(s) {
+		if unicode.IsUpper(c) {
+			return true
+		}
+	}
+	return false
+}

+ 9 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt

@@ -0,0 +1,9 @@
+r"""usage: prog [NAME_-2]..."""
+$ prog 10 20
+{"NAME_-2": ["10", "20"]}
+
+$ prog 10
+{"NAME_-2": ["10"]}
+
+$ prog
+{"NAME_-2": []}

+ 957 - 0
Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt

@@ -0,0 +1,957 @@
+r"""Usage: prog
+
+"""
+$ prog
+{}
+
+$ prog --xxx
+"user-error"
+
+
+r"""Usage: prog [options]
+
+Options: -a  All.
+
+"""
+$ prog
+{"-a": false}
+
+$ prog -a
+{"-a": true}
+
+$ prog -x
+"user-error"
+
+
+r"""Usage: prog [options]
+
+Options: --all  All.
+
+"""
+$ prog
+{"--all": false}
+
+$ prog --all
+{"--all": true}
+
+$ prog --xxx
+"user-error"
+
+
+r"""Usage: prog [options]
+
+Options: -v, --verbose  Verbose.
+
+"""
+$ prog --verbose
+{"--verbose": true}
+
+$ prog --ver
+{"--verbose": true}
+
+$ prog -v
+{"--verbose": true}
+
+
+r"""Usage: prog [options]
+
+Options: -p PATH
+
+"""
+$ prog -p home/
+{"-p": "home/"}
+
+$ prog -phome/
+{"-p": "home/"}
+
+$ prog -p
+"user-error"
+
+
+r"""Usage: prog [options]
+
+Options: --path <path>
+
+"""
+$ prog --path home/
+{"--path": "home/"}
+
+$ prog --path=home/
+{"--path": "home/"}
+
+$ prog --pa home/
+{"--path": "home/"}
+
+$ prog --pa=home/
+{"--path": "home/"}
+
+$ prog --path
+"user-error"
+
+
+r"""Usage: prog [options]
+
+Options: -p PATH, --path=<path>  Path to files.
+
+"""
+$ prog -proot
+{"--path": "root"}
+
+
+r"""Usage: prog [options]
+
+Options:    -p --path PATH  Path to files.
+
+"""
+$ prog -p root
+{"--path": "root"}
+
+$ prog --path root
+{"--path": "root"}
+
+
+r"""Usage: prog [options]
+
+Options:
+ -p PATH  Path to files [default: ./]
+
+"""
+$ prog
+{"-p": "./"}
+
+$ prog -phome
+{"-p": "home"}
+
+
+r"""UsAgE: prog [options]
+
+OpTiOnS: --path=<files>  Path to files
+                [dEfAuLt: /root]
+
+"""
+$ prog
+{"--path": "/root"}
+
+$ prog --path=home
+{"--path": "home"}
+
+
+r"""usage: prog [options]
+
+options:
+    -a        Add
+    -r        Remote
+    -m <msg>  Message
+
+"""
+$ prog -a -r -m Hello
+{"-a": true,
+ "-r": true,
+ "-m": "Hello"}
+
+$ prog -armyourass
+{"-a": true,
+ "-r": true,
+ "-m": "yourass"}
+
+$ prog -a -r
+{"-a": true,
+ "-r": true,
+ "-m": null}
+
+
+r"""Usage: prog [options]
+
+Options: --version
+         --verbose
+
+"""
+$ prog --version
+{"--version": true,
+ "--verbose": false}
+
+$ prog --verbose
+{"--version": false,
+ "--verbose": true}
+
+$ prog --ver
+"user-error"
+
+$ prog --verb
+{"--version": false,
+ "--verbose": true}
+
+
+r"""usage: prog [-a -r -m <msg>]
+
+options:
+ -a        Add
+ -r        Remote
+ -m <msg>  Message
+
+"""
+$ prog -armyourass
+{"-a": true,
+ "-r": true,
+ "-m": "yourass"}
+
+
+r"""usage: prog [-armmsg]
+
+options: -a        Add
+         -r        Remote
+         -m <msg>  Message
+
+"""
+$ prog -a -r -m Hello
+{"-a": true,
+ "-r": true,
+ "-m": "Hello"}
+
+
+r"""usage: prog -a -b
+
+options:
+ -a
+ -b
+
+"""
+$ prog -a -b
+{"-a": true, "-b": true}
+
+$ prog -b -a
+{"-a": true, "-b": true}
+
+$ prog -a
+"user-error"
+
+$ prog
+"user-error"
+
+
+r"""usage: prog (-a -b)
+
+options: -a
+         -b
+
+"""
+$ prog -a -b
+{"-a": true, "-b": true}
+
+$ prog -b -a
+{"-a": true, "-b": true}
+
+$ prog -a
+"user-error"
+
+$ prog
+"user-error"
+
+
+r"""usage: prog [-a] -b
+
+options: -a
+ -b
+
+"""
+$ prog -a -b
+{"-a": true, "-b": true}
+
+$ prog -b -a
+{"-a": true, "-b": true}
+
+$ prog -a
+"user-error"
+
+$ prog -b
+{"-a": false, "-b": true}
+
+$ prog
+"user-error"
+
+
+r"""usage: prog [(-a -b)]
+
+options: -a
+         -b
+
+"""
+$ prog -a -b
+{"-a": true, "-b": true}
+
+$ prog -b -a
+{"-a": true, "-b": true}
+
+$ prog -a
+"user-error"
+
+$ prog -b
+"user-error"
+
+$ prog
+{"-a": false, "-b": false}
+
+
+r"""usage: prog (-a|-b)
+
+options: -a
+         -b
+
+"""
+$ prog -a -b
+"user-error"
+
+$ prog
+"user-error"
+
+$ prog -a
+{"-a": true, "-b": false}
+
+$ prog -b
+{"-a": false, "-b": true}
+
+
+r"""usage: prog [ -a | -b ]
+
+options: -a
+         -b
+
+"""
+$ prog -a -b
+"user-error"
+
+$ prog
+{"-a": false, "-b": false}
+
+$ prog -a
+{"-a": true, "-b": false}
+
+$ prog -b
+{"-a": false, "-b": true}
+
+
+r"""usage: prog <arg>"""
+$ prog 10
+{"<arg>": "10"}
+
+$ prog 10 20
+"user-error"
+
+$ prog
+"user-error"
+
+
+r"""usage: prog [<arg>]"""
+$ prog 10
+{"<arg>": "10"}
+
+$ prog 10 20
+"user-error"
+
+$ prog
+{"<arg>": null}
+
+
+r"""usage: prog <kind> <name> <type>"""
+$ prog 10 20 40
+{"<kind>": "10", "<name>": "20", "<type>": "40"}
+
+$ prog 10 20
+"user-error"
+
+$ prog
+"user-error"
+
+
+r"""usage: prog <kind> [<name> <type>]"""
+$ prog 10 20 40
+{"<kind>": "10", "<name>": "20", "<type>": "40"}
+
+$ prog 10 20
+{"<kind>": "10", "<name>": "20", "<type>": null}
+
+$ prog
+"user-error"
+
+
+r"""usage: prog [<kind> | <name> <type>]"""
+$ prog 10 20 40
+"user-error"
+
+$ prog 20 40
+{"<kind>": null, "<name>": "20", "<type>": "40"}
+
+$ prog
+{"<kind>": null, "<name>": null, "<type>": null}
+
+
+r"""usage: prog (<kind> --all | <name>)
+
+options:
+ --all
+
+"""
+$ prog 10 --all
+{"<kind>": "10", "--all": true, "<name>": null}
+
+$ prog 10
+{"<kind>": null, "--all": false, "<name>": "10"}
+
+$ prog
+"user-error"
+
+
+r"""usage: prog [<name> <name>]"""
+$ prog 10 20
+{"<name>": ["10", "20"]}
+
+$ prog 10
+{"<name>": ["10"]}
+
+$ prog
+{"<name>": []}
+
+
+r"""usage: prog [(<name> <name>)]"""
+$ prog 10 20
+{"<name>": ["10", "20"]}
+
+$ prog 10
+"user-error"
+
+$ prog
+{"<name>": []}
+
+
+r"""usage: prog NAME..."""
+$ prog 10 20
+{"NAME": ["10", "20"]}
+
+$ prog 10
+{"NAME": ["10"]}
+
+$ prog
+"user-error"
+
+
+r"""usage: prog [NAME]..."""
+$ prog 10 20
+{"NAME": ["10", "20"]}
+
+$ prog 10
+{"NAME": ["10"]}
+
+$ prog
+{"NAME": []}
+
+
+r"""usage: prog [NAME...]"""
+$ prog 10 20
+{"NAME": ["10", "20"]}
+
+$ prog 10
+{"NAME": ["10"]}
+
+$ prog
+{"NAME": []}
+
+
+r"""usage: prog [NAME [NAME ...]]"""
+$ prog 10 20
+{"NAME": ["10", "20"]}
+
+$ prog 10
+{"NAME": ["10"]}
+
+$ prog
+{"NAME": []}
+
+
+r"""usage: prog (NAME | --foo NAME)
+
+options: --foo
+
+"""
+$ prog 10
+{"NAME": "10", "--foo": false}
+
+$ prog --foo 10
+{"NAME": "10", "--foo": true}
+
+$ prog --foo=10
+"user-error"
+
+
+r"""usage: prog (NAME | --foo) [--bar | NAME]
+
+options: --foo
+options: --bar
+
+"""
+$ prog 10
+{"NAME": ["10"], "--foo": false, "--bar": false}
+
+$ prog 10 20
+{"NAME": ["10", "20"], "--foo": false, "--bar": false}
+
+$ prog --foo --bar
+{"NAME": [], "--foo": true, "--bar": true}
+
+
+r"""Naval Fate.
+
+Usage:
+  prog ship new <name>...
+  prog ship [<name>] move <x> <y> [--speed=<kn>]
+  prog ship shoot <x> <y>
+  prog mine (set|remove) <x> <y> [--moored|--drifting]
+  prog -h | --help
+  prog --version
+
+Options:
+  -h --help     Show this screen.
+  --version     Show version.
+  --speed=<kn>  Speed in knots [default: 10].
+  --moored      Mored (anchored) mine.
+  --drifting    Drifting mine.
+
+"""
+$ prog ship Guardian move 150 300 --speed=20
+{"--drifting": false,
+ "--help": false,
+ "--moored": false,
+ "--speed": "20",
+ "--version": false,
+ "<name>": ["Guardian"],
+ "<x>": "150",
+ "<y>": "300",
+ "mine": false,
+ "move": true,
+ "new": false,
+ "remove": false,
+ "set": false,
+ "ship": true,
+ "shoot": false}
+
+
+r"""usage: prog --hello"""
+$ prog --hello
+{"--hello": true}
+
+
+r"""usage: prog [--hello=<world>]"""
+$ prog
+{"--hello": null}
+
+$ prog --hello wrld
+{"--hello": "wrld"}
+
+
+r"""usage: prog [-o]"""
+$ prog
+{"-o": false}
+
+$ prog -o
+{"-o": true}
+
+
+r"""usage: prog [-opr]"""
+$ prog -op
+{"-o": true, "-p": true, "-r": false}
+
+
+r"""usage: prog --aabb | --aa"""
+$ prog --aa
+{"--aabb": false, "--aa": true}
+
+$ prog --a
+"user-error"  # not a unique prefix
+
+#
+# Counting number of flags
+#
+
+r"""Usage: prog -v"""
+$ prog -v
+{"-v": true}
+
+
+r"""Usage: prog [-v -v]"""
+$ prog
+{"-v": 0}
+
+$ prog -v
+{"-v": 1}
+
+$ prog -vv
+{"-v": 2}
+
+
+r"""Usage: prog -v ..."""
+$ prog
+"user-error"
+
+$ prog -v
+{"-v": 1}
+
+$ prog -vv
+{"-v": 2}
+
+$ prog -vvvvvv
+{"-v": 6}
+
+
+r"""Usage: prog [-v | -vv | -vvv]
+
+This one is probably most readable user-friednly variant.
+
+"""
+$ prog
+{"-v": 0}
+
+$ prog -v
+{"-v": 1}
+
+$ prog -vv
+{"-v": 2}
+
+$ prog -vvvv
+"user-error"
+
+
+r"""usage: prog [--ver --ver]"""
+$ prog --ver --ver
+{"--ver": 2}
+
+
+#
+# Counting commands
+#
+
+r"""usage: prog [go]"""
+$ prog go
+{"go": true}
+
+
+r"""usage: prog [go go]"""
+$ prog
+{"go": 0}
+
+$ prog go
+{"go": 1}
+
+$ prog go go
+{"go": 2}
+
+$ prog go go go
+"user-error"
+
+r"""usage: prog go..."""
+$ prog go go go go go
+{"go": 5}
+
+#
+# [options] does not include options from usage-pattern
+#
+r"""usage: prog [options] [-a]
+
+options: -a
+         -b
+"""
+$ prog -a
+{"-a": true, "-b": false}
+
+$ prog -aa
+"user-error"
+
+#
+# Test [options] shourtcut
+#
+
+r"""Usage: prog [options] A
+Options:
+    -q  Be quiet
+    -v  Be verbose.
+
+"""
+$ prog arg
+{"A": "arg", "-v": false, "-q": false}
+
+$ prog -v arg
+{"A": "arg", "-v": true, "-q": false}
+
+$ prog -q arg
+{"A": "arg", "-v": false, "-q": true}
+
+#
+# Test single dash
+#
+
+r"""usage: prog [-]"""
+
+$ prog -
+{"-": true}
+
+$ prog
+{"-": false}
+
+#
+# If argument is repeated, its value should always be a list
+#
+
+r"""usage: prog [NAME [NAME ...]]"""
+
+$ prog a b
+{"NAME": ["a", "b"]}
+
+$ prog
+{"NAME": []}
+
+#
+# Option's argument defaults to null/None
+#
+
+r"""usage: prog [options]
+options:
+ -a        Add
+ -m <msg>  Message
+
+"""
+$ prog -a
+{"-m": null, "-a": true}
+
+#
+# Test options without description
+#
+
+r"""usage: prog --hello"""
+$ prog --hello
+{"--hello": true}
+
+r"""usage: prog [--hello=<world>]"""
+$ prog
+{"--hello": null}
+
+$ prog --hello wrld
+{"--hello": "wrld"}
+
+r"""usage: prog [-o]"""
+$ prog
+{"-o": false}
+
+$ prog -o
+{"-o": true}
+
+r"""usage: prog [-opr]"""
+$ prog -op
+{"-o": true, "-p": true, "-r": false}
+
+r"""usage: git [-v | --verbose]"""
+$ prog -v
+{"-v": true, "--verbose": false}
+
+r"""usage: git remote [-v | --verbose]"""
+$ prog remote -v
+{"remote": true, "-v": true, "--verbose": false}
+
+#
+# Test empty usage pattern
+#
+
+r"""usage: prog"""
+$ prog
+{}
+
+r"""usage: prog
+           prog <a> <b>
+"""
+$ prog 1 2
+{"<a>": "1", "<b>": "2"}
+
+$ prog
+{"<a>": null, "<b>": null}
+
+r"""usage: prog <a> <b>
+           prog
+"""
+$ prog
+{"<a>": null, "<b>": null}
+
+#
+# Option's argument should not capture default value from usage pattern
+#
+
+r"""usage: prog [--file=<f>]"""
+$ prog
+{"--file": null}
+
+r"""usage: prog [--file=<f>]
+
+options: --file <a>
+
+"""
+$ prog
+{"--file": null}
+
+r"""Usage: prog [-a <host:port>]
+
+Options: -a, --address <host:port>  TCP address [default: localhost:6283].
+
+"""
+$ prog
+{"--address": "localhost:6283"}
+
+#
+# If option with argument could be repeated,
+# its arguments should be accumulated into a list
+#
+
+r"""usage: prog --long=<arg> ..."""
+
+$ prog --long one
+{"--long": ["one"]}
+
+$ prog --long one --long two
+{"--long": ["one", "two"]}
+
+#
+# Test multiple elements repeated at once
+#
+
+r"""usage: prog (go <direction> --speed=<km/h>)..."""
+$ prog  go left --speed=5  go right --speed=9
+{"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]}
+
+#
+# Required options should work with option shortcut
+#
+
+r"""usage: prog [options] -a
+
+options: -a
+
+"""
+$ prog -a
+{"-a": true}
+
+#
+# If option could be repeated its defaults should be split into a list
+#
+
+r"""usage: prog [-o <o>]...
+
+options: -o <o>  [default: x]
+
+"""
+$ prog -o this -o that
+{"-o": ["this", "that"]}
+
+$ prog
+{"-o": ["x"]}
+
+r"""usage: prog [-o <o>]...
+
+options: -o <o>  [default: x y]
+
+"""
+$ prog -o this
+{"-o": ["this"]}
+
+$ prog
+{"-o": ["x", "y"]}
+
+#
+# Test stacked option's argument
+#
+
+r"""usage: prog -pPATH
+
+options: -p PATH
+
+"""
+$ prog -pHOME
+{"-p": "HOME"}
+
+#
+# Issue 56: Repeated mutually exclusive args give nested lists sometimes
+#
+
+r"""Usage: foo (--xx=x|--yy=y)..."""
+$ prog --xx=1 --yy=2
+{"--xx": ["1"], "--yy": ["2"]}
+
+#
+# POSIXly correct tokenization
+#
+
+r"""usage: prog [<input file>]"""
+$ prog f.txt
+{"<input file>": "f.txt"}
+
+r"""usage: prog [--input=<file name>]..."""
+$ prog --input a.txt --input=b.txt
+{"--input": ["a.txt", "b.txt"]}
+
+#
+# Issue 85: `[options]` shourtcut with multiple subcommands
+#
+
+r"""usage: prog good [options]
+           prog fail [options]
+
+options: --loglevel=N
+
+"""
+$ prog fail --loglevel 5
+{"--loglevel": "5", "fail": true, "good": false}
+
+#
+# Usage-section syntax
+#
+
+r"""usage:prog --foo"""
+$ prog --foo
+{"--foo": true}
+
+r"""PROGRAM USAGE: prog --foo"""
+$ prog --foo
+{"--foo": true}
+
+r"""Usage: prog --foo
+           prog --bar
+NOT PART OF SECTION"""
+$ prog --foo
+{"--foo": true, "--bar": false}
+
+r"""Usage:
+ prog --foo
+ prog --bar
+
+NOT PART OF SECTION"""
+$ prog --foo
+{"--foo": true, "--bar": false}
+
+r"""Usage:
+ prog --foo
+ prog --bar
+NOT PART OF SECTION"""
+$ prog --foo
+{"--foo": true, "--bar": false}
+
+#
+# Options-section syntax
+#
+
+r"""Usage: prog [options]
+
+global options: --foo
+local options: --baz
+               --bar
+other options:
+ --egg
+ --spam
+-not-an-option-
+
+"""
+$ prog --baz --egg
+{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false}

+ 1 - 2
Makefile

@@ -7,10 +7,9 @@ build: godep fmt frps frpc
 
 godep:
 	@go get github.com/tools/godep
-	godep restore
 
 fmt:
-	@GOPATH=$(NEW_GOPATH) godep go fmt ./...
+	GOPATH=$(NEW_GOPATH) godep go fmt ./...
 
 frps:
 	GOPATH=$(NEW_GOPATH) godep go build -o bin/frps ./src/frp/cmd/frps

+ 2 - 1
conf/frpc.ini

@@ -2,7 +2,8 @@
 [common]
 server_addr = 127.0.0.1
 server_port = 7000
-log_file = ./frpc.log
+# console or real logFile path like ./frpc.log
+log_file = console
 # debug, info, warn, error
 log_level = debug
 # file, console

+ 2 - 3
conf/frps.ini

@@ -2,11 +2,10 @@
 [common]
 bind_addr = 0.0.0.0
 bind_port = 7000
-log_file = ./frps.log
+# console or real logFile path like ./frps.log
+log_file = console
 # debug, info, warn, error
 log_level = debug
-# file, console
-log_way = file
 
 # test1 is the proxy name, client will use this name and passwd to connect to server
 [test1]

+ 61 - 1
src/frp/cmd/frps/main.go

@@ -1,19 +1,79 @@
 package main
 
 import (
+	"fmt"
 	"os"
+	"strconv"
+	"strings"
+
+	docopt "github.com/docopt/docopt-go"
 
 	"frp/models/server"
 	"frp/utils/conn"
 	"frp/utils/log"
+	"frp/utils/version"
+)
+
+var (
+	configFile string = "./frps.ini"
 )
 
+var usage string = `frps is the server of frp
+
+Usage: 
+	frps [-c config_file] [-L log_file] [--log-level=<log_level>] [--addr=<bind_addr>]
+	frps -h | --help | --version
+
+Options:
+	-c config_file            set config file
+	-L log_file               set output log file, including console
+	--log-level=<log_level>   set log level: debug, info, warn, error
+	--addr=<bind_addr>        listen addr for client, example: 0.0.0.0:7000
+	-h --help                 show this screen
+	--version                 show version
+`
+
 func main() {
-	err := server.LoadConf("./frps.ini")
+	// the configures parsed from file will be replaced by those from command line if exist
+	args, err := docopt.Parse(usage, nil, true, version.Full(), false)
+
+	if args["-c"] != nil {
+		configFile = args["-c"].(string)
+	}
+	err = server.LoadConf(configFile)
 	if err != nil {
+		fmt.Println(err)
 		os.Exit(-1)
 	}
 
+	if args["-L"] != nil {
+		if args["-L"].(string) == "console" {
+			server.LogWay = "console"
+		} else {
+			server.LogWay = "file"
+			server.LogFile = args["-L"].(string)
+		}
+	}
+
+	if args["--log-level"] != nil {
+		server.LogLevel = args["--log-level"].(string)
+	}
+
+	if args["--addr"] != nil {
+		addr := strings.Split(args["--addr"].(string), ":")
+		if len(addr) != 2 {
+			fmt.Println("--addr format error: example 0.0.0.0:7000")
+			os.Exit(1)
+		}
+		bindPort, err := strconv.ParseInt(addr[1], 10, 64)
+		if err != nil {
+			fmt.Println("--addr format error, example 0.0.0.0:7000")
+			os.Exit(1)
+		}
+		server.BindAddr = addr[0]
+		server.BindPort = bindPort
+	}
+
 	log.InitLog(server.LogWay, server.LogFile, server.LogLevel)
 
 	l, err := conn.Listen(server.BindAddr, server.BindPort)

+ 9 - 9
src/frp/models/server/config.go

@@ -10,10 +10,10 @@ import (
 // common config
 var (
 	BindAddr         string = "0.0.0.0"
-	BindPort         int64  = 9527
-	LogFile          string = "./frps.log"
-	LogLevel         string = "warn"
-	LogWay           string = "file"
+	BindPort         int64  = 7000
+	LogFile          string = "console"
+	LogWay           string = "console" // console or file
+	LogLevel         string = "info"
 	HeartBeatTimeout int64  = 30
 	UserConnTimeout  int64  = 10
 )
@@ -43,6 +43,11 @@ func LoadConf(confFile string) (err error) {
 	tmpStr, ok = conf.Get("common", "log_file")
 	if ok {
 		LogFile = tmpStr
+		if LogFile == "console" {
+			LogWay = "console"
+		} else {
+			LogWay = "file"
+		}
 	}
 
 	tmpStr, ok = conf.Get("common", "log_level")
@@ -50,11 +55,6 @@ func LoadConf(confFile string) (err error) {
 		LogLevel = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "log_way")
-	if ok {
-		LogWay = tmpStr
-	}
-
 	// servers
 	for name, section := range conf {
 		if name != "common" {