Merge branch 'complete-positional-rest-args'
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f19b2e8
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,35 @@
+language: go
+
+install:
+  # go-flags
+  - go get -d -v ./...
+  - go build -v ./...
+
+  # linting
+  - go get code.google.com/p/go.tools/cmd/vet
+  - go get github.com/golang/lint
+  - go install github.com/golang/lint/golint
+
+  # code coverage
+  - go get code.google.com/p/go.tools/cmd/cover
+  - go get github.com/onsi/ginkgo/ginkgo
+  - go get github.com/modocache/gover
+  - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then go get github.com/mattn/goveralls; fi
+
+script:
+  # go-flags
+  - $(exit $(gofmt -l . | wc -l))
+  - go test -v ./...
+
+  # linting
+  - go tool vet -all=true -v=true .
+  - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/golint .
+
+  # code coverage
+  - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/ginkgo -r -cover
+  - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/gover
+  - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
+
+env:
+  # coveralls.io
+  secure: "RCYbiB4P0RjQRIoUx/vG/AjP3mmYCbzOmr86DCww1Z88yNcy3hYr3Cq8rpPtYU5v0g7wTpu4adaKIcqRE9xknYGbqj3YWZiCoBP1/n4Z+9sHW3Dsd9D/GRGeHUus0laJUGARjWoCTvoEtOgTdGQDoX7mH+pUUY0FBltNYUdOiiU="
diff --git a/README.md b/README.md
index 7daf91c..b6faef6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
 go-flags: a go library for parsing command line arguments
 =========================================================
 
+[![GoDoc](https://godoc.org/github.com/jessevdk/go-flags?status.png)](https://godoc.org/github.com/jessevdk/go-flags) [![Build Status](https://travis-ci.org/jessevdk/go-flags.svg?branch=master)](https://travis-ci.org/jessevdk/go-flags) [![Coverage Status](https://img.shields.io/coveralls/jessevdk/go-flags.svg)](https://coveralls.io/r/jessevdk/go-flags?branch=master)
+
 This library provides similar functionality to the builtin flag library of
 go, but provides much more functionality and nicer formatting. From the
 documentation:
diff --git a/assert_test.go b/assert_test.go
index 2cb550d..8e06636 100644
--- a/assert_test.go
+++ b/assert_test.go
@@ -2,6 +2,10 @@
 
 import (
 	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
 	"path"
 	"runtime"
 	"testing"
@@ -123,3 +127,51 @@
 	assertError(t, err, typ, msg)
 	return ret
 }
+
+func diff(a, b string) (string, error) {
+	atmp, err := ioutil.TempFile("", "help-diff")
+
+	if err != nil {
+		return "", err
+	}
+
+	btmp, err := ioutil.TempFile("", "help-diff")
+
+	if err != nil {
+		return "", err
+	}
+
+	if _, err := io.WriteString(atmp, a); err != nil {
+		return "", err
+	}
+
+	if _, err := io.WriteString(btmp, b); err != nil {
+		return "", err
+	}
+
+	ret, err := exec.Command("diff", "-u", "-d", "--label", "got", atmp.Name(), "--label", "expected", btmp.Name()).Output()
+
+	os.Remove(atmp.Name())
+	os.Remove(btmp.Name())
+
+	if err.Error() == "exit status 1" {
+		return string(ret), nil
+	}
+
+	return string(ret), err
+}
+
+func assertDiff(t *testing.T, actual, expected, msg string) {
+	if actual == expected {
+		return
+	}
+
+	ret, err := diff(actual, expected)
+
+	if err != nil {
+		assertErrorf(t, "Unexpected diff error: %s", err)
+		assertErrorf(t, "Unexpected %s, expected:\n\n%s\n\nbut got\n\n%s", msg, expected, actual)
+	} else {
+		assertErrorf(t, "Unexpected %s:\n\n%s", msg, ret)
+	}
+}
diff --git a/closest.go b/closest.go
index 14f58d5..3b51875 100644
--- a/closest.go
+++ b/closest.go
@@ -9,35 +9,33 @@
 		return len(s)
 	}
 
-	var l1, l2, l3 int
-
-	if len(s) == 1 {
-		l1 = len(t) + 1
-	} else {
-		l1 = levenshtein(s[1:len(s)-1], t) + 1
+	dists := make([][]int, len(s)+1)
+	for i := range dists {
+		dists[i] = make([]int, len(t)+1)
+		dists[i][0] = i
 	}
 
-	if len(t) == 1 {
-		l2 = len(s) + 1
-	} else {
-		l2 = levenshtein(t[1:len(t)-1], s) + 1
+	for j := range t {
+		dists[0][j] = j
 	}
 
-	l3 = levenshtein(s[1:len(s)], t[1:len(t)])
-
-	if s[0] != t[0] {
-		l3++
+	for i, sc := range s {
+		for j, tc := range t {
+			if sc == tc {
+				dists[i+1][j+1] = dists[i][j]
+			} else {
+				dists[i+1][j+1] = dists[i][j] + 1
+				if dists[i+1][j] < dists[i+1][j+1] {
+					dists[i+1][j+1] = dists[i+1][j] + 1
+				}
+				if dists[i][j+1] < dists[i+1][j+1] {
+					dists[i+1][j+1] = dists[i][j+1] + 1
+				}
+			}
+		}
 	}
 
-	if l2 < l1 {
-		l1 = l2
-	}
-
-	if l1 < l3 {
-		return l1
-	}
-
-	return l3
+	return dists[len(s)][len(t)]
 }
 
 func closestChoice(cmd string, choices []string) (string, int) {
diff --git a/command_test.go b/command_test.go
index 8507782..a093e15 100644
--- a/command_test.go
+++ b/command_test.go
@@ -115,6 +115,23 @@
 	assertError(t, err, ErrCommandRequired, "Please specify one command of: add or remove")
 }
 
+func TestCommandEstimate2(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Cmd1 struct {
+		} `command:"remove"`
+
+		Cmd2 struct {
+		} `command:"add"`
+	}{}
+
+	p := NewParser(&opts, None)
+	_, err := p.ParseArgs([]string{"rmive"})
+
+	assertError(t, err, ErrUnknownCommand, "Unknown command `rmive', did you mean `remove'?")
+}
+
 type testCommand struct {
 	G        bool `short:"g"`
 	Executed bool
diff --git a/convert_test.go b/convert_test.go
index 9ee8ebb..0de0eea 100644
--- a/convert_test.go
+++ b/convert_test.go
@@ -171,13 +171,5 @@
       occaecat cupidatat non proident, sunt in culpa qui officia
       deserunt mollit anim id est laborum.`
 
-	if got != expected {
-		ret, err := helpDiff(got, expected)
-
-		if err != nil {
-			t.Errorf("Unexpected wrapped text, expected:\n\n%s\n\nbut got\n\n%s", expected, got)
-		} else {
-			t.Errorf("Unexpected wrapped text:\n\n%s", ret)
-		}
-	}
+	assertDiff(t, got, expected, "wrapped text")
 }
diff --git a/error.go b/error.go
index 3a67693..fce9d31 100644
--- a/error.go
+++ b/error.go
@@ -2,7 +2,6 @@
 
 import (
 	"fmt"
-	"reflect"
 )
 
 // ErrorType represents the type of error.
@@ -55,7 +54,36 @@
 )
 
 func (e ErrorType) String() string {
-	return reflect.TypeOf(e).Name()
+	switch e {
+	case ErrUnknown:
+		return "unknown"
+	case ErrExpectedArgument:
+		return "expected argument"
+	case ErrUnknownFlag:
+		return "unknown flag"
+	case ErrUnknownGroup:
+		return "unknown group"
+	case ErrMarshal:
+		return "marshal"
+	case ErrHelp:
+		return "help"
+	case ErrNoArgumentForBool:
+		return "no argument for bool"
+	case ErrRequired:
+		return "required"
+	case ErrShortNameTooLong:
+		return "short name too long"
+	case ErrDuplicatedFlag:
+		return "duplicated flag"
+	case ErrTag:
+		return "tag"
+	case ErrCommandRequired:
+		return "command required"
+	case ErrUnknownCommand:
+		return "unknown command"
+	}
+
+	return "unrecognized error type"
 }
 
 // Error represents a parser error. The error returned from Parse is of this
diff --git a/flags.go b/flags.go
index 830b1f7..923379b 100644
--- a/flags.go
+++ b/flags.go
@@ -17,6 +17,7 @@
     Options with long names (--verbose)
     Options with and without arguments (bool v.s. other type)
     Options with optional arguments and default values
+    Option default values from ENVIRONMENT_VARIABLES, including slice and map values
     Multiple option groups each containing a set of options
     Generate and print well-formatted help message
     Passing remaining command line arguments after -- (optional)
@@ -95,6 +96,12 @@
                     showing up in the help. If default-mask takes the special
                     value "-", then no default value will be shown at all
                     (optional)
+    env:            the default value of the option is overridden from the
+                    specified environment variable, if one has been defined.
+                    (optional)
+    env-delim:      the 'env' default value from environment is split into
+                    multiple values with the given delimiter string, use with
+                    slices and maps (optional)
     value-name:     the name of the argument value (to be shown in the help,
                     (optional)
 
@@ -219,6 +226,8 @@
 
     complete -F _completion_example completion-example
 
+Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set.
+
 Customized completion for argument values is supported by implementing
 the flags.Completer interface for the argument value type. An example
 of a type which does so is the flags.Filename type, an alias of string
diff --git a/group_private.go b/group_private.go
index 26ee92a..15251ce 100644
--- a/group_private.go
+++ b/group_private.go
@@ -124,6 +124,7 @@
 
 		description := mtag.Get("description")
 		def := mtag.GetMany("default")
+
 		optionalValue := mtag.GetMany("optional-value")
 		valueName := mtag.Get("value-name")
 		defaultMask := mtag.Get("default-mask")
@@ -136,6 +137,8 @@
 			ShortName:        short,
 			LongName:         longname,
 			Default:          def,
+			EnvDefaultKey:    mtag.Get("env"),
+			EnvDefaultDelim:  mtag.Get("env-delim"),
 			OptionalArgument: optional,
 			OptionalValue:    optionalValue,
 			Required:         required,
diff --git a/help.go b/help.go
index 0139215..d16f3b1 100644
--- a/help.go
+++ b/help.go
@@ -10,6 +10,7 @@
 	"fmt"
 	"io"
 	"reflect"
+	"runtime"
 	"strings"
 	"unicode/utf8"
 )
@@ -173,12 +174,24 @@
 			def = strings.Join(defs, ", ")
 		}
 
+		var envDef string
+		if option.EnvDefaultKey != "" {
+			var envPrintable string
+			if runtime.GOOS == "windows" {
+				envPrintable = "%" + option.EnvDefaultKey + "%"
+			} else {
+				envPrintable = "$" + option.EnvDefaultKey
+			}
+			envDef = fmt.Sprintf(" [%s]", envPrintable)
+		}
+
 		var desc string
 
 		if def != "" {
-			desc = fmt.Sprintf("%s (%v)", option.Description, def)
+			desc = fmt.Sprintf("%s (%v)%s", option.Description, def,
+				envDef)
 		} else {
-			desc = option.Description
+			desc = option.Description + envDef
 		}
 
 		writer.WriteString(wrapText(desc,
diff --git a/help_test.go b/help_test.go
index 27de672..def3043 100644
--- a/help_test.go
+++ b/help_test.go
@@ -3,48 +3,12 @@
 import (
 	"bytes"
 	"fmt"
-	"io"
-	"io/ioutil"
 	"os"
-	"os/exec"
 	"runtime"
 	"testing"
 	"time"
 )
 
-func helpDiff(a, b string) (string, error) {
-	atmp, err := ioutil.TempFile("", "help-diff")
-
-	if err != nil {
-		return "", err
-	}
-
-	btmp, err := ioutil.TempFile("", "help-diff")
-
-	if err != nil {
-		return "", err
-	}
-
-	if _, err := io.WriteString(atmp, a); err != nil {
-		return "", err
-	}
-
-	if _, err := io.WriteString(btmp, b); err != nil {
-		return "", err
-	}
-
-	ret, err := exec.Command("diff", "-u", "-d", "--label", "got", atmp.Name(), "--label", "expected", btmp.Name()).Output()
-
-	os.Remove(atmp.Name())
-	os.Remove(btmp.Name())
-
-	if err.Error() == "exit status 1" {
-		return string(ret), nil
-	}
-
-	return string(ret), err
-}
-
 type helpOptions struct {
 	Verbose          []bool       `short:"v" long:"verbose" description:"Show verbose debug information" ini-name:"verbose"`
 	Call             func(string) `short:"c" description:"Call phone number" ini-name:"call"`
@@ -54,6 +18,8 @@
 	Default      string            `long:"default" default:"Some value" description:"Test default value"`
 	DefaultArray []string          `long:"default-array" default:"Some value" default:"Another value" description:"Test default array value"`
 	DefaultMap   map[string]string `long:"default-map" default:"some:value" default:"another:value" description:"Testdefault map value"`
+	EnvDefault1  string            `long:"env-default1" default:"Some value" env:"ENV_DEFAULT" description:"Test env-default1 value"`
+	EnvDefault2  string            `long:"env-default2" env:"ENV_DEFAULT" description:"Test env-default2 value"`
 
 	OnlyIni string `ini-name:"only-ini" description:"Option only available in ini"`
 
@@ -81,8 +47,11 @@
 }
 
 func TestHelp(t *testing.T) {
-	var opts helpOptions
+	oldEnv := EnvSnapshot()
+	defer oldEnv.Restore()
+	os.Setenv("ENV_DEFAULT", "env-def")
 
+	var opts helpOptions
 	p := NewNamedParser("TestHelp", HelpFlag)
 	p.AddGroup("Application Options", "The application options", &opts)
 
@@ -113,6 +82,8 @@
       /default:            Test default value (Some value)
       /default-array:      Test default array value (Some value, Another value)
       /default-map:        Testdefault map value (some:value, another:value)
+      /env-default1:       Test env-default1 value (Some value) [%ENV_DEFAULT%]
+      /env-default2:       Test env-default2 value [%ENV_DEFAULT%]
 
 Other Options:
   /s:                      A slice of strings (some, value)
@@ -147,6 +118,8 @@
       --default=           Test default value (Some value)
       --default-array=     Test default array value (Some value, Another value)
       --default-map=       Testdefault map value (some:value, another:value)
+      --env-default1=      Test env-default1 value (Some value) [$ENV_DEFAULT]
+      --env-default2=      Test env-default2 value [$ENV_DEFAULT]
 
 Other Options:
   -s=                      A slice of strings (some, value)
@@ -170,22 +143,16 @@
 `
 		}
 
-		if e.Message != expected {
-			ret, err := helpDiff(e.Message, expected)
-
-			if err != nil {
-				t.Errorf("Unexpected diff error: %s", err)
-				t.Errorf("Unexpected help message, expected:\n\n%s\n\nbut got\n\n%s", expected, e.Message)
-			} else {
-				t.Errorf("Unexpected help message:\n\n%s", ret)
-			}
-		}
+		assertDiff(t, e.Message, expected, "help message")
 	}
 }
 
 func TestMan(t *testing.T) {
-	var opts helpOptions
+	oldEnv := EnvSnapshot()
+	defer oldEnv.Restore()
+	os.Setenv("ENV_DEFAULT", "env-def")
 
+	var opts helpOptions
 	p := NewNamedParser("TestMan", HelpFlag)
 	p.ShortDescription = "Test manpage generation"
 	p.LongDescription = "This is a somewhat `longer' description of what this does"
@@ -229,6 +196,12 @@
 \fB--default-map\fP
 Testdefault map value
 .TP
+\fB--env-default1\fP
+Test env-default1 value
+.TP
+\fB--env-default2\fP
+Test env-default2 value
+.TP
 \fB-s\fP
 A slice of strings
 .TP
@@ -246,6 +219,9 @@
 
 Longer \fBcommand\fP description
 
+\fBUsage\fP: TestMan [OPTIONS] command [command-OPTIONS]
+
+
 \fBAliases\fP: cm, cmd
 
 .TP
@@ -253,15 +229,7 @@
 Use for extra verbosity
 `, tt.Format("2 January 2006"))
 
-	if got != expected {
-		ret, err := helpDiff(got, expected)
-
-		if err != nil {
-			t.Errorf("Unexpected man page, expected:\n\n%s\n\nbut got\n\n%s", expected, got)
-		} else {
-			t.Errorf("Unexpected man page:\n\n%s", ret)
-		}
-	}
+	assertDiff(t, got, expected, "man page")
 }
 
 type helpCommandNoOptions struct {
@@ -270,8 +238,11 @@
 }
 
 func TestHelpCommand(t *testing.T) {
-	var opts helpCommandNoOptions
+	oldEnv := EnvSnapshot()
+	defer oldEnv.Restore()
+	os.Setenv("ENV_DEFAULT", "env-def")
 
+	var opts helpCommandNoOptions
 	p := NewNamedParser("TestHelpCommand", HelpFlag)
 	p.AddGroup("Application Options", "The application options", &opts)
 
@@ -307,15 +278,6 @@
 `
 		}
 
-		if e.Message != expected {
-			ret, err := helpDiff(e.Message, expected)
-
-			if err != nil {
-				t.Errorf("Unexpected diff error: %s", err)
-				t.Errorf("Unexpected help message, expected:\n\n%s\n\nbut got\n\n%s", expected, e.Message)
-			} else {
-				t.Errorf("Unexpected help message:\n\n%s", ret)
-			}
-		}
+		assertDiff(t, e.Message, expected, "help message")
 	}
 }
diff --git a/ini_private.go b/ini_private.go
index 1a671f5..2e72b87 100644
--- a/ini_private.go
+++ b/ini_private.go
@@ -58,13 +58,19 @@
 	return option.field.Name
 }
 
-func writeGroupIni(group *Group, namespace string, writer io.Writer, options IniOptions) {
+func writeGroupIni(cmd *Command, group *Group, namespace string, writer io.Writer, options IniOptions) {
 	var sname string
 
 	if len(namespace) != 0 {
-		sname = namespace + "." + group.ShortDescription
-	} else {
-		sname = group.ShortDescription
+		sname = namespace
+	}
+
+	if cmd.Group != group && len(group.ShortDescription) != 0 {
+		if len(sname) != 0 {
+			sname += "."
+		}
+
+		sname += group.ShortDescription
 	}
 
 	sectionwritten := false
@@ -154,7 +160,7 @@
 
 func writeCommandIni(command *Command, namespace string, writer io.Writer, options IniOptions) {
 	command.eachGroup(func(group *Group) {
-		writeGroupIni(group, namespace, writer, options)
+		writeGroupIni(command, group, namespace, writer, options)
 	})
 
 	for _, c := range command.commands {
diff --git a/ini_test.go b/ini_test.go
index eb752e9..e2b5cdc 100644
--- a/ini_test.go
+++ b/ini_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"io/ioutil"
 	"os"
 	"strings"
@@ -9,6 +10,10 @@
 )
 
 func TestWriteIni(t *testing.T) {
+	oldEnv := EnvSnapshot()
+	defer oldEnv.Restore()
+	os.Setenv("ENV_DEFAULT", "env-def")
+
 	var tests = []struct {
 		args     []string
 		options  IniOptions
@@ -22,6 +27,12 @@
 verbose = true
 verbose = true
 
+; Test env-default1 value
+EnvDefault1 = env-def
+
+; Test env-default2 value
+EnvDefault2 = env-def
+
 [Other Options]
 ; A map from string to int
 int-map = a:2
@@ -53,6 +64,12 @@
 DefaultMap = another:value
 DefaultMap = some:value
 
+; Test env-default1 value
+EnvDefault1 = env-def
+
+; Test env-default2 value
+EnvDefault2 = env-def
+
 ; Option only available in ini
 only-ini =
 
@@ -73,7 +90,7 @@
 ; This is a subsubgroup option
 Opt =
 
-[command.A command]
+[command]
 ; Use for extra verbosity
 ; ExtraVerbose =
 
@@ -102,6 +119,12 @@
 ; DefaultMap = another:value
 ; DefaultMap = some:value
 
+; Test env-default1 value
+EnvDefault1 = env-def
+
+; Test env-default2 value
+EnvDefault2 = env-def
+
 ; Option only available in ini
 ; only-ini =
 
@@ -121,7 +144,7 @@
 ; This is a subsubgroup option
 ; Opt =
 
-[command.A command]
+[command]
 ; Use for extra verbosity
 ; ExtraVerbose =
 
@@ -148,6 +171,12 @@
 ; Testdefault map value
 DefaultMap = new:value
 
+; Test env-default1 value
+EnvDefault1 = env-def
+
+; Test env-default2 value
+EnvDefault2 = env-def
+
 ; Option only available in ini
 ; only-ini =
 
@@ -167,7 +196,7 @@
 ; This is a subsubgroup option
 ; Opt =
 
-[command.A command]
+[command]
 ; Use for extra verbosity
 ; ExtraVerbose =
 
@@ -195,15 +224,8 @@
 		got := b.String()
 		expected := test.expected
 
-		if got != expected {
-			ret, err := helpDiff(got, expected)
-
-			if err != nil {
-				t.Errorf("Unexpected ini with arguments %+v and ini options %b, expected:\n\n%s\n\nbut got\n\n%s", test.args, test.options, expected, got)
-			} else {
-				t.Errorf("Unexpected ini with arguments %+v and ini options %b:\n\n%s", test.args, test.options, ret)
-			}
-		}
+		msg := fmt.Sprintf("with arguments %+v and ini options %b", test.args, test.options)
+		assertDiff(t, got, expected, msg)
 	}
 }
 
@@ -285,6 +307,7 @@
 
 [add.Other Options]
 other = subgroup
+
 `
 
 	b := strings.NewReader(inic)
@@ -301,6 +324,13 @@
 	}
 
 	assertString(t, opts.Add.Other.O, "subgroup")
+
+	// Test writing it back
+	buf := &bytes.Buffer{}
+
+	inip.Write(buf, IniDefault)
+
+	assertDiff(t, buf.String(), inic, "ini contents")
 }
 
 func TestIniNoIni(t *testing.T) {
@@ -383,9 +413,7 @@
 
 	expected := "[Application Options]\nValue = 123\n\n"
 
-	if string(found) != expected {
-		t.Fatalf("Expected file content to be \"%s\" but was \"%s\"", expected, found)
-	}
+	assertDiff(t, string(found), expected, "ini content")
 }
 
 func TestOverwriteRequiredOptions(t *testing.T) {
diff --git a/man.go b/man.go
index 3097156..e8e5916 100644
--- a/man.go
+++ b/man.go
@@ -74,11 +74,11 @@
 			nn = c.Name
 		}
 
-		writeManPageCommand(wr, nn, c)
+		writeManPageCommand(wr, nn, root, c)
 	}
 }
 
-func writeManPageCommand(wr io.Writer, name string, command *Command) {
+func writeManPageCommand(wr io.Writer, name string, root *Command, command *Command) {
 	fmt.Fprintf(wr, ".SS %s\n", name)
 	fmt.Fprintln(wr, command.ShortDescription)
 
@@ -98,6 +98,24 @@
 		}
 	}
 
+	var usage string
+	if us, ok := command.data.(Usage); ok {
+		usage = us.Usage()
+	} else if command.hasCliOptions() {
+		usage = fmt.Sprintf("[%s-OPTIONS]", command.Name)
+	}
+
+	var pre string
+	if root.hasCliOptions() {
+		pre = fmt.Sprintf("%s [OPTIONS] %s", root.Name, command.Name)
+	} else {
+		pre = fmt.Sprintf("%s %s", root.Name, command.Name)
+	}
+
+	if len(usage) > 0 {
+		fmt.Fprintf(wr, "\n\\fBUsage\\fP: %s %s\n\n", pre, usage)
+	}
+
 	if len(command.Aliases) > 0 {
 		fmt.Fprintf(wr, "\n\\fBAliases\\fP: %s\n\n", strings.Join(command.Aliases, ", "))
 	}
diff --git a/option.go b/option.go
index 325d9c8..26f2441 100644
--- a/option.go
+++ b/option.go
@@ -27,6 +27,12 @@
 	// The default value of the option.
 	Default []string
 
+	// The optional environment default value key name.
+	EnvDefaultKey string
+
+	// The optional delimiter string for EnvDefaultKey values.
+	EnvDefaultDelim string
+
 	// If true, specifies that the argument to an option flag is optional.
 	// When no argument to the flag is specified on the command line, the
 	// value of Default will be set in the field this option represents.
diff --git a/option_private.go b/option_private.go
index 6801885..d36c841 100644
--- a/option_private.go
+++ b/option_private.go
@@ -2,6 +2,8 @@
 
 import (
 	"reflect"
+	"strings"
+	"syscall"
 )
 
 // Set the value of an option to the specified value. An error will be returned
@@ -42,14 +44,30 @@
 }
 
 func (option *Option) empty() {
-	option.value.Set(option.emptyValue())
+	if !option.isFunc() {
+		option.value.Set(option.emptyValue())
+	}
 }
 
 func (option *Option) clearDefault() {
-	if len(option.Default) > 0 {
+	usedDefault := option.Default
+	if envKey := option.EnvDefaultKey; envKey != "" {
+		// os.Getenv() makes no distinction between undefined and
+		// empty values, so we use syscall.Getenv()
+		if value, ok := syscall.Getenv(envKey); ok {
+			if option.EnvDefaultDelim != "" {
+				usedDefault = strings.Split(value,
+					option.EnvDefaultDelim)
+			} else {
+				usedDefault = []string{value}
+			}
+		}
+	}
+
+	if len(usedDefault) > 0 {
 		option.empty()
 
-		for _, d := range option.Default {
+		for _, d := range usedDefault {
 			option.set(&d)
 		}
 	} else {
diff --git a/parser.go b/parser.go
index 2664f24..9156b5d 100644
--- a/parser.go
+++ b/parser.go
@@ -119,6 +119,8 @@
 
 	if len(os.Getenv("GO_FLAGS_COMPLETION")) != 0 {
 		p.AddCommand("__complete", "completion", "automatic flags completion", &completion{parser: p})
+
+		p.Options |= PassDoubleDash
 	}
 
 	return p
diff --git a/parser_private.go b/parser_private.go
index 4ebce4f..ee637e9 100644
--- a/parser_private.go
+++ b/parser_private.go
@@ -46,7 +46,7 @@
 func (p *parseState) checkRequired(parser *Parser) error {
 	c := parser.Command
 
-	required := make([]*Option, 0)
+	var required []*Option
 
 	for c != nil {
 		c.eachGroup(func(g *Group) {
@@ -62,7 +62,7 @@
 
 	if len(required) == 0 {
 		if len(p.positional) > 0 && p.command.ArgsRequired {
-			reqnames := make([]string, 0)
+			var reqnames []string
 
 			for _, arg := range p.positional {
 				if arg.isRemaining() {
@@ -255,22 +255,22 @@
 	return nil
 }
 
-func (s *parseState) addArgs(args ...string) error {
-	for len(s.positional) > 0 && len(args) > 0 {
-		arg := s.positional[0]
+func (p *parseState) addArgs(args ...string) error {
+	for len(p.positional) > 0 && len(args) > 0 {
+		arg := p.positional[0]
 
 		if err := convert(args[0], arg.value, arg.tag); err != nil {
 			return err
 		}
 
 		if !arg.isRemaining() {
-			s.positional = s.positional[1:]
+			p.positional = p.positional[1:]
 		}
 
 		args = args[1:]
 	}
 
-	s.retargs = append(s.retargs, args...)
+	p.retargs = append(p.retargs, args...)
 	return nil
 }
 
diff --git a/parser_test.go b/parser_test.go
index b227244..add87d8 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -1,7 +1,9 @@
 package flags
 
 import (
+	"os"
 	"reflect"
+	"strings"
 	"testing"
 	"time"
 )
@@ -96,3 +98,126 @@
 		}
 	}
 }
+
+// envRestorer keeps a copy of a set of env variables and can restore the env from them
+type envRestorer struct {
+	env map[string]string
+}
+
+func (r *envRestorer) Restore() {
+	os.Clearenv()
+	for k, v := range r.env {
+		os.Setenv(k, v)
+	}
+}
+
+// EnvSnapshot returns a snapshot of the currently set env variables
+func EnvSnapshot() *envRestorer {
+	r := envRestorer{make(map[string]string)}
+	for _, kv := range os.Environ() {
+		parts := strings.SplitN(kv, "=", 2)
+		if len(parts) != 2 {
+			panic("got a weird env variable: " + kv)
+		}
+		r.env[parts[0]] = parts[1]
+	}
+	return &r
+}
+
+type envDefaultOptions struct {
+	Int   int            `long:"i" default:"1" env:"TEST_I"`
+	Time  time.Duration  `long:"t" default:"1m" env:"TEST_T"`
+	Map   map[string]int `long:"m" default:"a:1" env:"TEST_M" env-delim:";"`
+	Slice []int          `long:"s" default:"1" default:"2" env:"TEST_S"  env-delim:","`
+}
+
+func TestEnvDefaults(t *testing.T) {
+	var tests = []struct {
+		msg      string
+		args     []string
+		expected envDefaultOptions
+		env      map[string]string
+	}{
+		{
+			msg:  "no arguments, no env, expecting default values",
+			args: []string{},
+			expected: envDefaultOptions{
+				Int:   1,
+				Time:  time.Minute,
+				Map:   map[string]int{"a": 1},
+				Slice: []int{1, 2},
+			},
+		},
+		{
+			msg:  "no arguments, env defaults, expecting env default values",
+			args: []string{},
+			expected: envDefaultOptions{
+				Int:   2,
+				Time:  2 * time.Minute,
+				Map:   map[string]int{"a": 2, "b": 3},
+				Slice: []int{4, 5, 6},
+			},
+			env: map[string]string{
+				"TEST_I": "2",
+				"TEST_T": "2m",
+				"TEST_M": "a:2;b:3",
+				"TEST_S": "4,5,6",
+			},
+		},
+		{
+			msg:  "non-zero value arguments, expecting overwritten arguments",
+			args: []string{"--i=3", "--t=3ms", "--m=c:3", "--s=3"},
+			expected: envDefaultOptions{
+				Int:   3,
+				Time:  3 * time.Millisecond,
+				Map:   map[string]int{"c": 3},
+				Slice: []int{3},
+			},
+			env: map[string]string{
+				"TEST_I": "2",
+				"TEST_T": "2m",
+				"TEST_M": "a:2;b:3",
+				"TEST_S": "4,5,6",
+			},
+		},
+		{
+			msg:  "zero value arguments, expecting overwritten arguments",
+			args: []string{"--i=0", "--t=0ms", "--m=:0", "--s=0"},
+			expected: envDefaultOptions{
+				Int:   0,
+				Time:  0,
+				Map:   map[string]int{"": 0},
+				Slice: []int{0},
+			},
+			env: map[string]string{
+				"TEST_I": "2",
+				"TEST_T": "2m",
+				"TEST_M": "a:2;b:3",
+				"TEST_S": "4,5,6",
+			},
+		},
+	}
+
+	oldEnv := EnvSnapshot()
+	defer oldEnv.Restore()
+
+	for _, test := range tests {
+		var opts envDefaultOptions
+		oldEnv.Restore()
+		for envKey, envValue := range test.env {
+			os.Setenv(envKey, envValue)
+		}
+		_, err := ParseArgs(&opts, test.args)
+		if err != nil {
+			t.Fatalf("%s:\nUnexpected error: %v", test.msg, err)
+		}
+
+		if opts.Slice == nil {
+			opts.Slice = []int{}
+		}
+
+		if !reflect.DeepEqual(opts, test.expected) {
+			t.Errorf("%s:\nUnexpected options with arguments %+v\nexpected\n%+v\nbut got\n%+v\n", test.msg, test.args, test.expected, opts)
+		}
+	}
+}