Merge remote-tracking branch 'mvo5/master'
diff --git a/command_private.go b/command_private.go
index 2107ee0..0a77ab2 100644
--- a/command_private.go
+++ b/command_private.go
@@ -104,6 +104,16 @@
 	return c.scanType(c.scanSubcommandHandler(c.Group))
 }
 
+func (c *Command) eachOption(f func(*Command, *Group, *Option)) {
+	c.eachCommand(func(c *Command) {
+		c.eachGroup(func(g *Group) {
+			for _, option := range g.options {
+				f(c, g, option)
+			}
+		})
+	}, true)
+}
+
 func (c *Command) eachCommand(f func(*Command), recurse bool) {
 	f(c)
 
@@ -144,6 +154,28 @@
 		commands:   make(map[string]*Command),
 	}
 
+	parent := c.parent
+
+	var parents []*Command
+
+	for parent != nil {
+		if cmd, ok := parent.(*Command); ok {
+			parents = append(parents, cmd)
+			parent = cmd.parent
+		} else {
+			parent = nil
+		}
+	}
+
+	for i := len(parents) - 1; i >= 0; i-- {
+		parents[i].fillLookup(&ret, true)
+	}
+
+	c.fillLookup(&ret, false)
+	return ret
+}
+
+func (c *Command) fillLookup(ret *lookup, onlyOptions bool) {
 	c.eachGroup(func(g *Group) {
 		for _, option := range g.options {
 			if option.ShortName != 0 {
@@ -156,6 +188,10 @@
 		}
 	})
 
+	if onlyOptions {
+		return
+	}
+
 	for _, subcommand := range c.commands {
 		ret.commands[subcommand.Name] = subcommand
 
@@ -163,8 +199,6 @@
 			ret.commands[a] = subcommand
 		}
 	}
-
-	return ret
 }
 
 func (c *Command) groupByName(name string) *Group {
diff --git a/command_test.go b/command_test.go
index a093e15..e64ac45 100644
--- a/command_test.go
+++ b/command_test.go
@@ -95,7 +95,135 @@
 		} `command:"cmd"`
 	}{}
 
-	assertParseFail(t, ErrUnknownFlag, "unknown flag `v'", &opts, "cmd", "-v", "-g")
+	assertParseSuccess(t, &opts, "cmd", "-v", "-g")
+
+	if !opts.Value {
+		t.Errorf("Expected Value to be true")
+	}
+
+	if !opts.Command.G {
+		t.Errorf("Expected Command.G to be true")
+	}
+}
+
+func TestCommandFlagOrderSub(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Command struct {
+			G bool `short:"g"`
+
+			SubCommand struct {
+				B bool `short:"b"`
+			} `command:"sub"`
+		} `command:"cmd"`
+	}{}
+
+	assertParseSuccess(t, &opts, "cmd", "sub", "-v", "-g", "-b")
+
+	if !opts.Value {
+		t.Errorf("Expected Value to be true")
+	}
+
+	if !opts.Command.G {
+		t.Errorf("Expected Command.G to be true")
+	}
+
+	if !opts.Command.SubCommand.B {
+		t.Errorf("Expected Command.SubCommand.B to be true")
+	}
+}
+
+func TestCommandFlagOverride1(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Command struct {
+			Value bool `short:"v"`
+		} `command:"cmd"`
+	}{}
+
+	assertParseSuccess(t, &opts, "-v", "cmd")
+
+	if !opts.Value {
+		t.Errorf("Expected Value to be true")
+	}
+
+	if opts.Command.Value {
+		t.Errorf("Expected Command.Value to be false")
+	}
+}
+
+func TestCommandFlagOverride2(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Command struct {
+			Value bool `short:"v"`
+		} `command:"cmd"`
+	}{}
+
+	assertParseSuccess(t, &opts, "cmd", "-v")
+
+	if opts.Value {
+		t.Errorf("Expected Value to be false")
+	}
+
+	if !opts.Command.Value {
+		t.Errorf("Expected Command.Value to be true")
+	}
+}
+
+func TestCommandFlagOverrideSub(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Command struct {
+			Value bool `short:"v"`
+
+			SubCommand struct {
+				Value bool `short:"v"`
+			} `command:"sub"`
+		} `command:"cmd"`
+	}{}
+
+	assertParseSuccess(t, &opts, "cmd", "sub", "-v")
+
+	if opts.Value {
+		t.Errorf("Expected Value to be false")
+	}
+
+	if opts.Command.Value {
+		t.Errorf("Expected Command.Value to be false")
+	}
+
+	if !opts.Command.SubCommand.Value {
+		t.Errorf("Expected Command.Value to be true")
+	}
+}
+
+func TestCommandFlagOverrideSub2(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Command struct {
+			Value bool `short:"v"`
+
+			SubCommand struct {
+				G bool `short:"g"`
+			} `command:"sub"`
+		} `command:"cmd"`
+	}{}
+
+	assertParseSuccess(t, &opts, "cmd", "sub", "-v")
+
+	if opts.Value {
+		t.Errorf("Expected Value to be false")
+	}
+
+	if !opts.Command.Value {
+		t.Errorf("Expected Command.Value to be true")
+	}
 }
 
 func TestCommandEstimate(t *testing.T) {
diff --git a/completion.go b/completion.go
index d0adfe0..894f1d6 100644
--- a/completion.go
+++ b/completion.go
@@ -43,8 +43,6 @@
 
 type completion struct {
 	parser *Parser
-
-	ShowDescriptions bool
 }
 
 // Filename is a string alias which provides filename completion.
@@ -275,19 +273,17 @@
 	return ret
 }
 
-func (c *completion) execute(args []string) {
-	ret := c.complete(args)
-
-	if c.ShowDescriptions && len(ret) > 1 {
+func (c *completion) print(items []Completion, showDescriptions bool) {
+	if showDescriptions && len(items) > 1 {
 		maxl := 0
 
-		for _, v := range ret {
+		for _, v := range items {
 			if len(v.Item) > maxl {
 				maxl = len(v.Item)
 			}
 		}
 
-		for _, v := range ret {
+		for _, v := range items {
 			fmt.Printf("%s", v.Item)
 
 			if len(v.Description) > 0 {
@@ -297,7 +293,7 @@
 			fmt.Printf("\n")
 		}
 	} else {
-		for _, v := range ret {
+		for _, v := range items {
 			fmt.Println(v.Item)
 		}
 	}
diff --git a/completion_test.go b/completion_test.go
index 2d5a97f..f440fd7 100644
--- a/completion_test.go
+++ b/completion_test.go
@@ -268,6 +268,11 @@
 
 		p := NewParser(&completionTestOptions, None)
 
+		p.CompletionHandler = func(items []Completion) {
+			comp := &completion{parser: p}
+			comp.print(items, test.ShowDescriptions)
+		}
+
 		_, err := p.ParseArgs(test.Args)
 
 		w.Close()
diff --git a/convert.go b/convert.go
index 191b5f4..938c3ac 100644
--- a/convert.go
+++ b/convert.go
@@ -312,6 +312,26 @@
 	return s
 }
 
+func quoteIfNeededV(s []string) []string {
+	ret := make([]string, len(s))
+
+	for i, v := range s {
+		ret[i] = quoteIfNeeded(v)
+	}
+
+	return ret
+}
+
+func quoteV(s []string) []string {
+	ret := make([]string, len(s))
+
+	for i, v := range s {
+		ret[i] = strconv.Quote(v)
+	}
+
+	return ret
+}
+
 func unquoteIfPossible(s string) (string, error) {
 	if len(s) == 0 || s[0] != '"' {
 		return s, nil
@@ -319,39 +339,3 @@
 
 	return strconv.Unquote(s)
 }
-
-func wrapText(s string, l int, prefix string) string {
-	// Basic text wrapping of s at spaces to fit in l
-	var ret string
-
-	s = strings.TrimSpace(s)
-
-	for len(s) > l {
-		// Try to split on space
-		suffix := ""
-
-		pos := strings.LastIndex(s[:l], " ")
-
-		if pos < 0 {
-			pos = l - 1
-			suffix = "-\n"
-		}
-
-		if len(ret) != 0 {
-			ret += "\n" + prefix
-		}
-
-		ret += strings.TrimSpace(s[:pos]) + suffix
-		s = strings.TrimSpace(s[pos:])
-	}
-
-	if len(s) > 0 {
-		if len(ret) != 0 {
-			ret += "\n" + prefix
-		}
-
-		return ret + s
-	}
-
-	return ret
-}
diff --git a/convert_test.go b/convert_test.go
index 0de0eea..ef131dc 100644
--- a/convert_test.go
+++ b/convert_test.go
@@ -157,19 +157,3 @@
 
 	assertError(t, err, ErrMarshal, "strconv.ParseInt: parsing \"no\": invalid syntax")
 }
-
-func TestWrapText(t *testing.T) {
-	s := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
-
-	got := wrapText(s, 60, "      ")
-	expected := `Lorem ipsum dolor sit amet, consectetur adipisicing elit,
-      sed do eiusmod tempor incididunt ut labore et dolore magna
-      aliqua. Ut enim ad minim veniam, quis nostrud exercitation
-      ullamco laboris nisi ut aliquip ex ea commodo consequat.
-      Duis aute irure dolor in reprehenderit in voluptate velit
-      esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
-      occaecat cupidatat non proident, sunt in culpa qui officia
-      deserunt mollit anim id est laborum.`
-
-	assertDiff(t, got, expected, "wrapped text")
-}
diff --git a/error.go b/error.go
index fce9d31..2f27aee 100644
--- a/error.go
+++ b/error.go
@@ -51,6 +51,10 @@
 
 	// ErrUnknownCommand indicates that an unknown command was specified.
 	ErrUnknownCommand
+
+	// ErrInvalidChoice indicates an invalid option value which only allows
+	// a certain number of choices.
+	ErrInvalidChoice
 )
 
 func (e ErrorType) String() string {
@@ -81,6 +85,8 @@
 		return "command required"
 	case ErrUnknownCommand:
 		return "unknown command"
+	case ErrInvalidChoice:
+		return "invalid choice"
 	}
 
 	return "unrecognized error type"
diff --git a/flags.go b/flags.go
index 7bdcdb4..6656848 100644
--- a/flags.go
+++ b/flags.go
@@ -84,7 +84,9 @@
                       displayed in generated man pages (optional)
     no-flag:          if non-empty this field is ignored as an option (optional)
 
-    optional:       whether an argument of the option is optional (optional)
+    optional:       whether an argument of the option is optional. When an
+                    argument is optional it can only be specified using
+                    --option=argument (optional)
     optional-value: the value of an optional option when the option occurs
                     without an argument. This tag can be specified multiple
                     times in the case of maps or slices (optional)
@@ -104,7 +106,9 @@
                     slices and maps (optional)
     value-name:     the name of the argument value (to be shown in the help)
                     (optional)
-    hidden:         the option is not visible in the help output
+    choice:         limits the values for an option to a set of values.
+                    This tag can be specified mltiple times (optional)
+    hidden:         the option is not visible in the help or man page.
 
     base: a base (radix) used to convert strings to integer values, the
           default base is 10 (i.e. decimal) (optional)
@@ -139,7 +143,6 @@
                           of the positional argument placeholder to be shown in
                           the help (optional)
 
-
 Either the `short:` tag or the `long:` must be specified to make the field eligible as an
 option.
 
@@ -184,12 +187,16 @@
 remaining command line arguments.
 
 Command structs can have options which become valid to parse after the
-command has been specified on the command line. It is currently not valid
-to specify options from the parent level of the command after the command
-name has occurred. Thus, given a top-level option "-v" and a command "add":
+command has been specified on the command line, in addition to the options
+of all the parent commands. I.e. considering a -v flag on the parser and an
+add command, the following are equivalent:
 
-    Valid:   ./app -v add
-    Invalid: ./app add -v
+    ./app -v add
+    ./app add -v
+
+However, if the -v flag is defined on the add command, then the first of
+the two examples above would fail since the -v flag is not defined before
+the add command.
 
 
 Completion
diff --git a/group_private.go b/group_private.go
index 569c81b..96c3619 100644
--- a/group_private.go
+++ b/group_private.go
@@ -131,6 +131,7 @@
 
 		optional := (mtag.Get("optional") != "")
 		required := (mtag.Get("required") != "")
+		choices := mtag.GetMany("choice")
 		hidden := (mtag.Get("hidden") != "")
 
 		option := &Option{
@@ -145,6 +146,7 @@
 			Required:         required,
 			ValueName:        valueName,
 			DefaultMask:      defaultMask,
+			Choices:          choices,
 			Hidden:           hidden,
 
 			group: g,
diff --git a/help.go b/help.go
index 88c4b5f..fab5450 100644
--- a/help.go
+++ b/help.go
@@ -9,7 +9,6 @@
 	"bytes"
 	"fmt"
 	"io"
-	"reflect"
 	"runtime"
 	"strings"
 	"unicode/utf8"
@@ -92,13 +91,71 @@
 				ret.hasValueName = true
 			}
 
-			ret.updateLen(info.LongNameWithNamespace()+info.ValueName, c != p.Command)
+			l := info.LongNameWithNamespace() + info.ValueName
+
+			if len(info.Choices) != 0 {
+				l += "[" + strings.Join(info.Choices, "|") + "]"
+			}
+
+			ret.updateLen(l, c != p.Command)
 		}
 	})
 
 	return ret
 }
 
+func wrapText(s string, l int, prefix string) string {
+	var ret string
+
+	// Basic text wrapping of s at spaces to fit in l
+	lines := strings.Split(s, "\n")
+
+	for _, line := range lines {
+		var retline string
+
+		line = strings.TrimSpace(line)
+
+		for len(line) > l {
+			// Try to split on space
+			suffix := ""
+
+			pos := strings.LastIndex(line[:l], " ")
+
+			if pos < 0 {
+				pos = l - 1
+				suffix = "-\n"
+			}
+
+			if len(retline) != 0 {
+				retline += "\n" + prefix
+			}
+
+			retline += strings.TrimSpace(line[:pos]) + suffix
+			line = strings.TrimSpace(line[pos:])
+		}
+
+		if len(line) > 0 {
+			if len(retline) != 0 {
+				retline += "\n" + prefix
+			}
+
+			retline += line
+		}
+
+		if len(ret) > 0 {
+			ret += "\n"
+
+			if len(retline) > 0 {
+				ret += prefix
+			}
+		}
+
+		ret += retline
+	}
+
+	return ret
+}
+
 func (p *Parser) writeHelpOption(writer *bufio.Writer, option *Option, info alignmentInfo) {
 	line := &bytes.Buffer{}
 
@@ -140,6 +197,10 @@
 		if len(option.ValueName) > 0 {
 			line.WriteString(option.ValueName)
 		}
+
+		if len(option.Choices) > 0 {
+			line.WriteString("[" + strings.Join(option.Choices, "|") + "]")
+		}
 	}
 
 	written := line.Len()
@@ -149,39 +210,12 @@
 		dw := descstart - written
 		writer.WriteString(strings.Repeat(" ", dw))
 
-		def := ""
-		defs := option.Default
+		var def string
 
-		if len(option.DefaultMask) != 0 {
-			if option.DefaultMask != "-" {
-				def = option.DefaultMask
-			}
-		} else if len(defs) == 0 && option.canArgument() {
-			var showdef bool
-
-			switch option.field.Type.Kind() {
-			case reflect.Func, reflect.Ptr:
-				showdef = !option.value.IsNil()
-			case reflect.Slice, reflect.String, reflect.Array:
-				showdef = option.value.Len() > 0
-			case reflect.Map:
-				showdef = !option.value.IsNil() && option.value.Len() > 0
-			default:
-				zeroval := reflect.Zero(option.field.Type)
-				showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
-			}
-
-			if showdef {
-				def, _ = convertToString(option.value, option.tag)
-			}
-		} else if len(defs) != 0 {
-			l := len(defs) - 1
-
-			for i := 0; i < l; i++ {
-				def += quoteIfNeeded(defs[i]) + ", "
-			}
-
-			def += quoteIfNeeded(defs[l])
+		if len(option.DefaultMask) != 0 && option.DefaultMask != "-" {
+			def = option.DefaultMask
+		} else {
+			def = option.defaultLiteral
 		}
 
 		var envDef string
@@ -198,7 +232,7 @@
 		var desc string
 
 		if def != "" {
-			desc = fmt.Sprintf("%s (%v)%s", option.Description, def, envDef)
+			desc = fmt.Sprintf("%s (default: %v)%s", option.Description, def, envDef)
 		} else {
 			desc = option.Description + envDef
 		}
diff --git a/help_test.go b/help_test.go
index c802883..33f67a3 100644
--- a/help_test.go
+++ b/help_test.go
@@ -21,6 +21,7 @@
 	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"`
 	OptionWithArgName string            `long:"opt-with-arg-name" value-name:"something" description:"Option with named argument"`
+	OptionWithChoices string            `long:"opt-with-choices" value-name:"choice" choice:"dog" choice:"cat" description:"Option with choices"`
 
 	OnlyIni string `ini-name:"only-ini" description:"Option only available in ini"`
 
@@ -76,34 +77,42 @@
   TestHelp [OPTIONS] [filename] [num] <command>
 
 Application Options:
-  /v, /verbose                         Show verbose debug information
-  /c:                                  Call phone number
-      /ptrslice:                       A slice of pointers to string
+  /v, /verbose                              Show verbose debug information
+  /c:                                       Call phone number
+      /ptrslice:                            A slice of pointers to string
       /empty-description
-      /default:                        Test default value ("Some\nvalue")
-      /default-array:                  Test default array value (Some value, "Other\tvalue")
-      /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%]
-      /opt-with-arg-name:something     Option with named argument
+      /default:                             Test default value (default:
+                                            "Some\nvalue")
+      /default-array:                       Test default array value (default:
+                                            Some value, "Other\tvalue")
+      /default-map:                         Testdefault map value (default:
+                                            some:value, another:value)
+      /env-default1:                        Test env-default1 value (default:
+                                            Some value) [%ENV_DEFAULT%]
+      /env-default2:                        Test env-default2 value
+                                            [%ENV_DEFAULT%]
+      /opt-with-arg-name:something          Option with named argument
+      /opt-with-choices:choice[dog|cat]     Option with choices
 
 Other Options:
-  /s:                                  A slice of strings (some, value)
-      /intmap:                         A map from string to int (a:1)
+  /s:                                       A slice of strings (default: some,
+                                            value)
+      /intmap:                              A map from string to int (default:
+                                            a:1)
 
 Subgroup:
-      /sip.opt:                        This is a subgroup option
+      /sip.opt:                             This is a subgroup option
 
 Subsubgroup:
-      /sip.sap.opt:                    This is a subsubgroup option
+      /sip.sap.opt:                         This is a subsubgroup option
 
 Help Options:
-  /?                                   Show this help message
-  /h, /help                            Show this help message
+  /?                                        Show this help message
+  /h, /help                                 Show this help message
 
 Arguments:
-  filename:                            A filename
-  num:                                 A number
+  filename:                                 A filename
+  num:                                      A number
 
 Available commands:
   command  A command (aliases: cm, cmd)
@@ -113,36 +122,41 @@
   TestHelp [OPTIONS] [filename] [num] <command>
 
 Application Options:
-  -v, --verbose                        Show verbose debug information
-  -c=                                  Call phone number
-      --ptrslice=                      A slice of pointers to string
+  -v, --verbose                             Show verbose debug information
+  -c=                                       Call phone number
+      --ptrslice=                           A slice of pointers to string
       --empty-description
-      --default=                       Test default value ("Some\nvalue")
-      --default-array=                 Test default array value (Some value,
-                                       "Other\tvalue")
-      --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]
-      --opt-with-arg-name=something    Option with named argument
+      --default=                            Test default value (default:
+                                            "Some\nvalue")
+      --default-array=                      Test default array value (default:
+                                            Some value, "Other\tvalue")
+      --default-map=                        Testdefault map value (default:
+                                            some:value, another:value)
+      --env-default1=                       Test env-default1 value (default:
+                                            Some value) [$ENV_DEFAULT]
+      --env-default2=                       Test env-default2 value
+                                            [$ENV_DEFAULT]
+      --opt-with-arg-name=something         Option with named argument
+      --opt-with-choices=choice[dog|cat]    Option with choices
 
 Other Options:
-  -s=                                  A slice of strings (some, value)
-      --intmap=                        A map from string to int (a:1)
+  -s=                                       A slice of strings (default: some,
+                                            value)
+      --intmap=                             A map from string to int (default:
+                                            a:1)
 
 Subgroup:
-      --sip.opt=                       This is a subgroup option
+      --sip.opt=                            This is a subgroup option
 
 Subsubgroup:
-      --sip.sap.opt=                   This is a subsubgroup option
+      --sip.sap.opt=                        This is a subsubgroup option
 
 Help Options:
-  -h, --help                           Show this help message
+  -h, --help                                Show this help message
 
 Arguments:
-  filename:                            A filename
-  num:                                 A number
+  filename:                                 A filename
+  num:                                      A number
 
 Available commands:
   command  A command (aliases: cm, cmd)
@@ -173,6 +187,14 @@
 
 	tt := time.Now()
 
+	var envDefaultName string
+
+	if runtime.GOOS == "windows" {
+		envDefaultName = "%ENV_DEFAULT%"
+	} else {
+		envDefaultName = "$ENV_DEFAULT"
+	}
+
 	expected := fmt.Sprintf(`.TH TestMan 1 "%s"
 .SH NAME
 TestMan \- Test manpage generation
@@ -182,45 +204,48 @@
 This is a somewhat \fBlonger\fP description of what this does
 .SH OPTIONS
 .TP
-\fB-v, --verbose\fP
+\fB\fB\-v\fR, \fB\-\-verbose\fR\fP
 Show verbose debug information
 .TP
-\fB-c\fP
+\fB\fB\-c\fR\fP
 Call phone number
 .TP
-\fB--ptrslice\fP
+\fB\fB\-\-ptrslice\fR\fP
 A slice of pointers to string
 .TP
-\fB--empty-description\fP
+\fB\fB\-\-empty-description\fR\fP
 .TP
-\fB--default\fP
+\fB\fB\-\-default\fR <default: \fI"Some\\nvalue"\fR>\fP
 Test default value
 .TP
-\fB--default-array\fP
+\fB\fB\-\-default-array\fR <default: \fI"Some value", "Other\\tvalue"\fR>\fP
 Test default array value
 .TP
-\fB--default-map\fP
+\fB\fB\-\-default-map\fR <default: \fI"some:value", "another:value"\fR>\fP
 Testdefault map value
 .TP
-\fB--env-default1\fP
+\fB\fB\-\-env-default1\fR <default: \fI"Some value"\fR>\fP
 Test env-default1 value
 .TP
-\fB--env-default2\fP
+\fB\fB\-\-env-default2\fR <default: \fI%s\fR>\fP
 Test env-default2 value
 .TP
-\fB--opt-with-arg-name\fP
+\fB\fB\-\-opt-with-arg-name\fR \fIsomething\fR\fP
 Option with named argument
 .TP
-\fB-s\fP
+\fB\fB\-\-opt-with-choices\fR \fIchoice\fR\fP
+Option with choices
+.TP
+\fB\fB\-s\fR <default: \fI"some", "value"\fR>\fP
 A slice of strings
 .TP
-\fB--intmap\fP
+\fB\fB\-\-intmap\fR <default: \fI"a:1"\fR>\fP
 A map from string to int
 .TP
-\fB--sip.opt\fP
+\fB\fB\-\-sip.opt\fR\fP
 This is a subgroup option
 .TP
-\fB--sip.sap.opt\fP
+\fB\fB\-\-sip.sap.opt\fR\fP
 This is a subsubgroup option
 .SH COMMANDS
 .SS command
@@ -229,14 +254,14 @@
 Longer \fBcommand\fP description
 
 \fBUsage\fP: TestMan [OPTIONS] command [command-OPTIONS]
-
+.TP
 
 \fBAliases\fP: cm, cmd
 
 .TP
-\fB--extra-verbose\fP
+\fB\fB\-\-extra-verbose\fR\fP
 Use for extra verbosity
-`, tt.Format("2 January 2006"))
+`, tt.Format("2 January 2006"), envDefaultName)
 
 	assertDiff(t, got, expected, "man page")
 }
@@ -290,3 +315,136 @@
 		assertDiff(t, e.Message, expected, "help message")
 	}
 }
+
+func TestHelpDefaults(t *testing.T) {
+	var expected string
+
+	if runtime.GOOS == "windows" {
+		expected = `Usage:
+  TestHelpDefaults [OPTIONS]
+
+Application Options:
+      /with-default:               With default (default: default-value)
+      /without-default:            Without default
+      /with-programmatic-default:  With programmatic default (default:
+                                   default-value)
+
+Help Options:
+  /?                               Show this help message
+  /h, /help                        Show this help message
+`
+	} else {
+		expected = `Usage:
+  TestHelpDefaults [OPTIONS]
+
+Application Options:
+      --with-default=              With default (default: default-value)
+      --without-default=           Without default
+      --with-programmatic-default= With programmatic default (default:
+                                   default-value)
+
+Help Options:
+  -h, --help                       Show this help message
+`
+	}
+
+	tests := []struct {
+		Args   []string
+		Output string
+	}{
+		{
+			Args:   []string{"-h"},
+			Output: expected,
+		},
+		{
+			Args:   []string{"--with-default", "other-value", "--with-programmatic-default", "other-value", "-h"},
+			Output: expected,
+		},
+	}
+
+	for _, test := range tests {
+		var opts struct {
+			WithDefault             string `long:"with-default" default:"default-value" description:"With default"`
+			WithoutDefault          string `long:"without-default" description:"Without default"`
+			WithProgrammaticDefault string `long:"with-programmatic-default" description:"With programmatic default"`
+		}
+
+		opts.WithProgrammaticDefault = "default-value"
+
+		p := NewNamedParser("TestHelpDefaults", HelpFlag)
+		p.AddGroup("Application Options", "The application options", &opts)
+
+		_, err := p.ParseArgs(test.Args)
+
+		if err == nil {
+			t.Fatalf("Expected help error")
+		}
+
+		if e, ok := err.(*Error); !ok {
+			t.Fatalf("Expected flags.Error, but got %T", err)
+		} else {
+			if e.Type != ErrHelp {
+				t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type)
+			}
+
+			assertDiff(t, e.Message, test.Output, "help message")
+		}
+	}
+}
+
+func TestHelpRestArgs(t *testing.T) {
+	opts := struct {
+		Verbose bool `short:"v"`
+	}{}
+
+	p := NewNamedParser("TestHelpDefaults", HelpFlag)
+	p.AddGroup("Application Options", "The application options", &opts)
+
+	retargs, err := p.ParseArgs([]string{"-h", "-v", "rest"})
+
+	if err == nil {
+		t.Fatalf("Expected help error")
+	}
+
+	assertStringArray(t, retargs, []string{"-v", "rest"})
+}
+
+func TestWrapText(t *testing.T) {
+	s := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+
+	got := wrapText(s, 60, "      ")
+	expected := `Lorem ipsum dolor sit amet, consectetur adipisicing elit,
+      sed do eiusmod tempor incididunt ut labore et dolore magna
+      aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+      ullamco laboris nisi ut aliquip ex ea commodo consequat.
+      Duis aute irure dolor in reprehenderit in voluptate velit
+      esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+      occaecat cupidatat non proident, sunt in culpa qui officia
+      deserunt mollit anim id est laborum.`
+
+	assertDiff(t, got, expected, "wrapped text")
+}
+
+func TestWrapParagraph(t *testing.T) {
+	s := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\n"
+	s += "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n\n"
+	s += "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\n"
+	s += "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"
+
+	got := wrapText(s, 60, "      ")
+	expected := `Lorem ipsum dolor sit amet, consectetur adipisicing elit,
+      sed do eiusmod tempor incididunt ut labore et dolore magna
+      aliqua.
+
+      Ut enim ad minim veniam, quis nostrud exercitation ullamco
+      laboris nisi ut aliquip ex ea commodo consequat.
+
+      Duis aute irure dolor in reprehenderit in voluptate velit
+      esse cillum dolore eu fugiat nulla pariatur.
+
+      Excepteur sint occaecat cupidatat non proident, sunt in
+      culpa qui officia deserunt mollit anim id est laborum.
+`
+
+	assertDiff(t, got, expected, "wrapped paragraph")
+}
diff --git a/ini_test.go b/ini_test.go
index 215b757..aa3b556 100644
--- a/ini_test.go
+++ b/ini_test.go
@@ -74,6 +74,9 @@
 ; Option with named argument
 OptionWithArgName =
 
+; Option with choices
+OptionWithChoices =
+
 ; Option only available in ini
 only-ini =
 
@@ -132,6 +135,9 @@
 ; Option with named argument
 ; OptionWithArgName =
 
+; Option with choices
+; OptionWithChoices =
+
 ; Option only available in ini
 ; only-ini =
 
@@ -187,6 +193,9 @@
 ; Option with named argument
 ; OptionWithArgName =
 
+; Option with choices
+; OptionWithChoices =
+
 ; Option only available in ini
 ; only-ini =
 
diff --git a/man.go b/man.go
index e8e5916..cd4cadd 100644
--- a/man.go
+++ b/man.go
@@ -3,30 +3,35 @@
 import (
 	"fmt"
 	"io"
+	"runtime"
 	"strings"
 	"time"
 )
 
+func manQuote(s string) string {
+	return strings.Replace(s, "\\", "\\\\", -1)
+}
+
 func formatForMan(wr io.Writer, s string) {
 	for {
 		idx := strings.IndexRune(s, '`')
 
 		if idx < 0 {
-			fmt.Fprintf(wr, "%s", s)
+			fmt.Fprintf(wr, "%s", manQuote(s))
 			break
 		}
 
-		fmt.Fprintf(wr, "%s", s[:idx])
+		fmt.Fprintf(wr, "%s", manQuote(s[:idx]))
 
 		s = s[idx+1:]
 		idx = strings.IndexRune(s, '\'')
 
 		if idx < 0 {
-			fmt.Fprintf(wr, "%s", s)
+			fmt.Fprintf(wr, "%s", manQuote(s))
 			break
 		}
 
-		fmt.Fprintf(wr, "\\fB%s\\fP", s[:idx])
+		fmt.Fprintf(wr, "\\fB%s\\fP", manQuote(s[:idx]))
 		s = s[idx+1:]
 	}
 }
@@ -42,7 +47,7 @@
 			fmt.Fprintf(wr, "\\fB")
 
 			if opt.ShortName != 0 {
-				fmt.Fprintf(wr, "-%c", opt.ShortName)
+				fmt.Fprintf(wr, "\\fB\\-%c\\fR", opt.ShortName)
 			}
 
 			if len(opt.LongName) != 0 {
@@ -50,10 +55,33 @@
 					fmt.Fprintf(wr, ", ")
 				}
 
-				fmt.Fprintf(wr, "--%s", opt.LongNameWithNamespace())
+				fmt.Fprintf(wr, "\\fB\\-\\-%s\\fR", manQuote(opt.LongNameWithNamespace()))
+			}
+
+			if len(opt.ValueName) != 0 || opt.OptionalArgument {
+				if opt.OptionalArgument {
+					fmt.Fprintf(wr, " [\\fI%s=%s\\fR]", manQuote(opt.ValueName), manQuote(strings.Join(quoteV(opt.OptionalValue), ", ")))
+				} else {
+					fmt.Fprintf(wr, " \\fI%s\\fR", manQuote(opt.ValueName))
+				}
+			}
+
+			if len(opt.Default) != 0 {
+				fmt.Fprintf(wr, " <default: \\fI%s\\fR>", manQuote(strings.Join(quoteV(opt.Default), ", ")))
+			} else if len(opt.EnvDefaultKey) != 0 {
+				if runtime.GOOS == "windows" {
+					fmt.Fprintf(wr, " <default: \\fI%%%s%%\\fR>", manQuote(opt.EnvDefaultKey))
+				} else {
+					fmt.Fprintf(wr, " <default: \\fI$%s\\fR>", manQuote(opt.EnvDefaultKey))
+				}
+			}
+
+			if opt.Required {
+				fmt.Fprintf(wr, " (\\fIrequired\\fR)")
 			}
 
 			fmt.Fprintln(wr, "\\fP")
+
 			if len(opt.Description) != 0 {
 				formatForMan(wr, opt.Description)
 				fmt.Fprintln(wr, "")
@@ -85,10 +113,10 @@
 	if len(command.LongDescription) > 0 {
 		fmt.Fprintln(wr, "")
 
-		cmdstart := fmt.Sprintf("The %s command", command.Name)
+		cmdstart := fmt.Sprintf("The %s command", manQuote(command.Name))
 
 		if strings.HasPrefix(command.LongDescription, cmdstart) {
-			fmt.Fprintf(wr, "The \\fI%s\\fP command", command.Name)
+			fmt.Fprintf(wr, "The \\fI%s\\fP command", manQuote(command.Name))
 
 			formatForMan(wr, command.LongDescription[len(cmdstart):])
 			fmt.Fprintln(wr, "")
@@ -113,11 +141,11 @@
 	}
 
 	if len(usage) > 0 {
-		fmt.Fprintf(wr, "\n\\fBUsage\\fP: %s %s\n\n", pre, usage)
+		fmt.Fprintf(wr, "\n\\fBUsage\\fP: %s %s\n.TP\n", manQuote(pre), manQuote(usage))
 	}
 
 	if len(command.Aliases) > 0 {
-		fmt.Fprintf(wr, "\n\\fBAliases\\fP: %s\n\n", strings.Join(command.Aliases, ", "))
+		fmt.Fprintf(wr, "\n\\fBAliases\\fP: %s\n\n", manQuote(strings.Join(command.Aliases, ", ")))
 	}
 
 	writeManPageOptions(wr, command.Group)
@@ -129,9 +157,9 @@
 func (p *Parser) WriteManPage(wr io.Writer) {
 	t := time.Now()
 
-	fmt.Fprintf(wr, ".TH %s 1 \"%s\"\n", p.Name, t.Format("2 January 2006"))
+	fmt.Fprintf(wr, ".TH %s 1 \"%s\"\n", manQuote(p.Name), t.Format("2 January 2006"))
 	fmt.Fprintln(wr, ".SH NAME")
-	fmt.Fprintf(wr, "%s \\- %s\n", p.Name, p.ShortDescription)
+	fmt.Fprintf(wr, "%s \\- %s\n", manQuote(p.Name), manQuote(p.ShortDescription))
 	fmt.Fprintln(wr, ".SH SYNOPSIS")
 
 	usage := p.Usage
@@ -140,7 +168,7 @@
 		usage = "[OPTIONS]"
 	}
 
-	fmt.Fprintf(wr, "\\fB%s\\fP %s\n", p.Name, usage)
+	fmt.Fprintf(wr, "\\fB%s\\fP %s\n", manQuote(p.Name), manQuote(usage))
 	fmt.Fprintln(wr, ".SH DESCRIPTION")
 
 	formatForMan(wr, p.LongDescription)
diff --git a/option.go b/option.go
index 00fb461..afad7e4 100644
--- a/option.go
+++ b/option.go
@@ -35,7 +35,7 @@
 
 	// 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.
+	// value of OptionalValue will be set in the field this option represents.
 	// This is only valid for non-boolean options.
 	OptionalArgument bool
 
@@ -62,6 +62,9 @@
 	// passwords.
 	DefaultMask string
 
+	// If non empty, only a certain set of values is allowed for an option.
+	Choices []string
+
 	// The group which the option belongs to
 	group *Group
 
@@ -76,6 +79,8 @@
 
 	tag   multiTag
 	isSet bool
+
+	defaultLiteral string
 }
 
 // LongNameWithNamespace returns the option's long name with the group namespaces
@@ -158,3 +163,8 @@
 func (option *Option) Value() interface{} {
 	return option.value.Interface()
 }
+
+// IsSet returns true if option has been set
+func (option *Option) IsSet() bool {
+	return option.isSet
+}
diff --git a/option_private.go b/option_private.go
index d36c841..ab63ba2 100644
--- a/option_private.go
+++ b/option_private.go
@@ -12,6 +12,29 @@
 func (option *Option) set(value *string) error {
 	option.isSet = true
 
+	if len(option.Choices) != 0 {
+		found := false
+
+		for _, choice := range option.Choices {
+			if choice == *value {
+				found = true
+				break
+			}
+		}
+
+		if !found {
+			allowed := strings.Join(option.Choices[0:len(option.Choices)-1], ", ")
+
+			if len(option.Choices) > 1 {
+				allowed += " or " + option.Choices[len(option.Choices)-1]
+			}
+
+			return newErrorf(ErrInvalidChoice,
+				"Invalid value `%s' for option `%s'. Allowed values are: %s",
+				*value, option, allowed)
+		}
+	}
+
 	if option.isFunc() {
 		return option.call(value)
 	} else if value != nil {
@@ -180,3 +203,38 @@
 
 	return nil
 }
+
+func (option *Option) updateDefaultLiteral() {
+	defs := option.Default
+	def := ""
+
+	if len(defs) == 0 && option.canArgument() {
+		var showdef bool
+
+		switch option.field.Type.Kind() {
+		case reflect.Func, reflect.Ptr:
+			showdef = !option.value.IsNil()
+		case reflect.Slice, reflect.String, reflect.Array:
+			showdef = option.value.Len() > 0
+		case reflect.Map:
+			showdef = !option.value.IsNil() && option.value.Len() > 0
+		default:
+			zeroval := reflect.Zero(option.field.Type)
+			showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
+		}
+
+		if showdef {
+			def, _ = convertToString(option.value, option.tag)
+		}
+	} else if len(defs) != 0 {
+		l := len(defs) - 1
+
+		for i := 0; i < l; i++ {
+			def += quoteIfNeeded(defs[i]) + ", "
+		}
+
+		def += quoteIfNeeded(defs[l])
+	}
+
+	option.defaultLiteral = def
+}
diff --git a/parser.go b/parser.go
index 6f45a03..8dedd0e 100644
--- a/parser.go
+++ b/parser.go
@@ -32,6 +32,11 @@
 	// or an error to indicate a parse failure.
 	UnknownOptionHandler func(option string, arg SplitArgument, args []string) ([]string, error)
 
+	// CompletionHandler is a function gets called to handle the completion of
+	// items. By default, the items are printed and the application is exited.
+	// You can override this default behavior by specifying a custom CompletionHandler.
+	CompletionHandler func(items []Completion)
+
 	internalError error
 }
 
@@ -162,14 +167,18 @@
 //
 // When the common help group has been added (AddHelp) and either -h or --help
 // was specified in the command line arguments, a help message will be
-// automatically printed. Furthermore, the special error type ErrHelp is returned.
+// automatically printed if the PrintErrors option is enabled.
+// Furthermore, the special error type ErrHelp is returned.
 // It is up to the caller to exit the program if so desired.
 func (p *Parser) ParseArgs(args []string) ([]string, error) {
 	if p.internalError != nil {
 		return nil, p.internalError
 	}
 
-	p.clearIsSet()
+	p.eachOption(func(c *Command, g *Group, option *Option) {
+		option.isSet = false
+		option.updateDefaultLiteral()
+	})
 
 	// Add built-in help group to all commands if necessary
 	if (p.Options & HelpFlag) != None {
@@ -180,13 +189,15 @@
 
 	if len(compval) != 0 {
 		comp := &completion{parser: p}
+		items := comp.complete(args)
 
-		if compval == "verbose" {
-			comp.ShowDescriptions = true
+		if p.CompletionHandler != nil {
+			p.CompletionHandler(items)
+		} else {
+			comp.print(items, compval == "verbose")
+			os.Exit(0)
 		}
 
-		comp.execute(args)
-
 		return nil, nil
 	}
 
@@ -253,17 +264,13 @@
 	}
 
 	if s.err == nil {
-		p.eachCommand(func(c *Command) {
-			c.eachGroup(func(g *Group) {
-				for _, option := range g.options {
-					if option.isSet {
-						continue
-					}
+		p.eachOption(func(c *Command, g *Group, option *Option) {
+			if option.isSet {
+				return
+			}
 
-					option.clearDefault()
-				}
-			})
-		}, true)
+			option.clearDefault()
+		})
 
 		s.checkRequired(p)
 	}
@@ -279,7 +286,15 @@
 	}
 
 	if reterr != nil {
-		return append([]string{s.arg}, s.args...), p.printError(reterr)
+		var retargs []string
+
+		if ourErr, ok := reterr.(*Error); !ok || ourErr.Type != ErrHelp {
+			retargs = append([]string{s.arg}, s.args...)
+		} else {
+			retargs = s.args
+		}
+
+		return retargs, p.printError(reterr)
 	}
 
 	return s.retargs, nil
diff --git a/parser_test.go b/parser_test.go
index 5792872..fe6865c 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -14,6 +14,11 @@
 	Int        int `long:"i"`
 	IntDefault int `long:"id" default:"1"`
 
+	Float64        float64 `long:"f"`
+	Float64Default float64 `long:"fd" default:"-3.14"`
+
+	NumericFlag bool `short:"3" default:"false"`
+
 	String            string `long:"str"`
 	StringDefault     string `long:"strd" default:"abc"`
 	StringNotUnquoted string `long:"strnot" unquote:"false"`
@@ -41,6 +46,11 @@
 				Int:        0,
 				IntDefault: 1,
 
+				Float64:        0.0,
+				Float64Default: -3.14,
+
+				NumericFlag: false,
+
 				String:        "",
 				StringDefault: "abc",
 
@@ -56,11 +66,16 @@
 		},
 		{
 			msg:  "non-zero value arguments, expecting overwritten arguments",
-			args: []string{"--i=3", "--id=3", "--str=def", "--strd=def", "--t=3ms", "--td=3ms", "--m=c:3", "--md=c:3", "--s=3", "--sd=3"},
+			args: []string{"--i=3", "--id=3", "--f=-2.71", "--fd=2.71", "-3", "--str=def", "--strd=def", "--t=3ms", "--td=3ms", "--m=c:3", "--md=c:3", "--s=3", "--sd=3"},
 			expected: defaultOptions{
 				Int:        3,
 				IntDefault: 3,
 
+				Float64:        -2.71,
+				Float64Default: 2.71,
+
+				NumericFlag: true,
+
 				String:        "def",
 				StringDefault: "def",
 
@@ -76,11 +91,14 @@
 		},
 		{
 			msg:  "zero value arguments, expecting overwritten arguments",
-			args: []string{"--i=0", "--id=0", "--str", "", "--strd=\"\"", "--t=0ms", "--td=0s", "--m=:0", "--md=:0", "--s=0", "--sd=0"},
+			args: []string{"--i=0", "--id=0", "--f=0", "--fd=0", "--str", "", "--strd=\"\"", "--t=0ms", "--td=0s", "--m=:0", "--md=:0", "--s=0", "--sd=0"},
 			expected: defaultOptions{
 				Int:        0,
 				IntDefault: 0,
 
+				Float64:        0,
+				Float64Default: 0,
+
 				String:        "",
 				StringDefault: "",
 
@@ -320,21 +338,21 @@
 			args:        []string{"--string-slice", "foobar", "--string-slice", "-o"},
 			expectError: true,
 			errType:     ErrExpectedArgument,
-			errMsg:      "expected argument for flag `--string-slice', but got option `-o'",
+			errMsg:      "expected argument for flag `" + defaultLongOptDelimiter + "string-slice', but got option `-o'",
 		},
 		{
 			// long option must not be accepted as argument
 			args:        []string{"--string-slice", "foobar", "--string-slice", "--other-option"},
 			expectError: true,
 			errType:     ErrExpectedArgument,
-			errMsg:      "expected argument for flag `--string-slice', but got option `--other-option'",
+			errMsg:      "expected argument for flag `" + defaultLongOptDelimiter + "string-slice', but got option `--other-option'",
 		},
 		{
 			// long option must not be accepted as argument
 			args:        []string{"--string-slice", "--"},
 			expectError: true,
 			errType:     ErrExpectedArgument,
-			errMsg:      "expected argument for flag `--string-slice', but got double dash `--'",
+			errMsg:      "expected argument for flag `" + defaultLongOptDelimiter + "string-slice', but got double dash `--'",
 		},
 		{
 			// quoted and appended option should be accepted as argument (even if it looks like an option)
@@ -345,6 +363,20 @@
 			args: []string{"--string-slice", "-"},
 		},
 		{
+			// Do not accept arguments which start with '-' even if the next character is a digit
+			args:        []string{"--string-slice", "-3.14"},
+			expectError: true,
+			errType:     ErrExpectedArgument,
+			errMsg:      "expected argument for flag `" + defaultLongOptDelimiter + "string-slice', but got option `-3.14'",
+		},
+		{
+			// Do not accept arguments which start with '-' if the next character is not a digit
+			args:        []string{"--string-slice", "-character"},
+			expectError: true,
+			errType:     ErrExpectedArgument,
+			errMsg:      "expected argument for flag `" + defaultLongOptDelimiter + "string-slice', but got option `-character'",
+		},
+		{
 			args: []string{"-o", "-", "-"},
 			rest: []string{"-", "-"},
 		},
@@ -429,3 +461,13 @@
 		assertErrorf(t, "Parser should have returned error, but returned nil")
 	}
 }
+
+func TestChoices(t *testing.T) {
+	var opts struct {
+		Choice string `long:"choose" choice:"v1" choice:"v2"`
+	}
+
+	assertParseFail(t, ErrInvalidChoice, "Invalid value `invalid' for option `"+defaultLongOptDelimiter+"choose'. Allowed values are: v1 or v2", &opts, "--choose", "invalid")
+	assertParseSuccess(t, &opts, "--choose", "v2")
+	assertString(t, opts.Choice, "v2")
+}