Added support for positional arguments
diff --git a/arg.go b/arg.go
new file mode 100644
index 0000000..f436278
--- /dev/null
+++ b/arg.go
@@ -0,0 +1,17 @@
+package flags
+
+import (
+	"reflect"
+)
+
+type Arg struct {
+	Name        string
+	Description string
+
+	value reflect.Value
+	tag   multiTag
+}
+
+func (a *Arg) isRemaining() bool {
+	return a.value.Type().Kind() == reflect.Slice
+}
diff --git a/arg_test.go b/arg_test.go
new file mode 100644
index 0000000..faea280
--- /dev/null
+++ b/arg_test.go
@@ -0,0 +1,53 @@
+package flags
+
+import (
+	"testing"
+)
+
+func TestPositional(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Positional struct {
+			Command  int
+			Filename string
+			Rest     []string
+		} `positional-args:"yes" required:"yes"`
+	}{}
+
+	p := NewParser(&opts, Default)
+	ret, err := p.ParseArgs([]string{"10", "arg_test.go", "a", "b"})
+
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+		return
+	}
+
+	if opts.Positional.Command != 10 {
+		t.Fatalf("Expected opts.Positional.Command to be 10, but got %v", opts.Positional.Command)
+	}
+
+	if opts.Positional.Filename != "arg_test.go" {
+		t.Fatalf("Expected opts.Positional.Filename to be \"arg_test.go\", but got %v", opts.Positional.Filename)
+	}
+
+	assertStringArray(t, opts.Positional.Rest, []string{"a", "b"})
+	assertStringArray(t, ret, []string{})
+}
+
+func TestPositionalRequired(t *testing.T) {
+	var opts = struct {
+		Value bool `short:"v"`
+
+		Positional struct {
+			Command  int
+			Filename string
+			Rest     []string
+		} `positional-args:"yes" required:"yes"`
+	}{}
+
+	p := NewParser(&opts, None)
+	_, err := p.ParseArgs([]string{"10"})
+
+	assertError(t, err, ErrRequired, "the required argument `Filename` was not provided")
+}
diff --git a/command.go b/command.go
index 16369c4..d6dbc42 100644
--- a/command.go
+++ b/command.go
@@ -20,8 +20,12 @@
 	// Aliases for the command
 	Aliases []string
 
+	// Whether positional arguments are required
+	ArgsRequired bool
+
 	commands            []*Command
 	hasBuiltinHelpGroup bool
+	args                []*Arg
 }
 
 // Commander is an interface which can be implemented by any command added in
@@ -92,3 +96,11 @@
 
 	return nil
 }
+
+// Args returns a list of positional arguments associated with this command
+func (c *Command) Args() []*Arg {
+	ret := make([]*Arg, len(c.args))
+	copy(ret, c.args)
+
+	return ret
+}
diff --git a/command_private.go b/command_private.go
index b5d60ab..36d564b 100644
--- a/command_private.go
+++ b/command_private.go
@@ -29,6 +29,44 @@
 			return true, err
 		}
 
+		positional := mtag.Get("positional-args")
+
+		if len(positional) != 0 {
+			stype := realval.Type()
+
+			for i := 0; i < stype.NumField(); i++ {
+				field := stype.Field(i)
+
+				m := newMultiTag((string(field.Tag)))
+
+				if err := m.Parse(); err != nil {
+					return true, err
+				}
+
+				name := m.Get("name")
+
+				if len(name) == 0 {
+					name = field.Name
+				}
+
+				arg := &Arg{
+					Name:        name,
+					Description: m.Get("description"),
+
+					value: realval.Field(i),
+					tag:   m,
+				}
+
+				c.args = append(c.args, arg)
+
+				if len(mtag.Get("required")) != 0 {
+					c.ArgsRequired = true
+				}
+			}
+
+			return true, nil
+		}
+
 		subcommand := mtag.Get("command")
 
 		if len(subcommand) != 0 {
diff --git a/example_test.go b/example_test.go
index b06dd27..b5bf67a 100644
--- a/example_test.go
+++ b/example_test.go
@@ -4,7 +4,6 @@
 import (
 	"fmt"
 	"os/exec"
-	"strings"
 )
 
 func Example() {
@@ -36,6 +35,13 @@
 
 		// Example of a map
 		IntMap map[string]int `long:"intmap" description:"A map from string to int"`
+
+		// Example of positional arguments
+		Args struct {
+			Id   string
+			Num  int
+			Rest []string
+		} `positional-args:"yes" required:"yes"`
 	}
 
 	// Callback which will invoke callto:<argument> to call a number.
@@ -59,15 +65,16 @@
 		"--ptrslice", "world",
 		"--intmap", "a:1",
 		"--intmap", "b:5",
-		"arg1",
-		"arg2",
-		"arg3",
+		"id",
+		"10",
+		"remaining1",
+		"remaining2",
 	}
 
 	// Parse flags from `args'. Note that here we use flags.ParseArgs for
 	// the sake of making a working example. Normally, you would simply use
 	// flags.Parse(&opts) which uses os.Args
-	args, err := ParseArgs(&opts, args)
+	_, err := ParseArgs(&opts, args)
 
 	if err != nil {
 		panic(err)
@@ -80,7 +87,9 @@
 	fmt.Printf("StringSlice: %v\n", opts.StringSlice)
 	fmt.Printf("PtrSlice: [%v %v]\n", *opts.PtrSlice[0], *opts.PtrSlice[1])
 	fmt.Printf("IntMap: [a:%v b:%v]\n", opts.IntMap["a"], opts.IntMap["b"])
-	fmt.Printf("Remaining args: %s\n", strings.Join(args, " "))
+	fmt.Printf("Args.Id: %s\n", opts.Args.Id)
+	fmt.Printf("Args.Num: %d\n", opts.Args.Num)
+	fmt.Printf("Args.Rest: %v\n", opts.Args.Rest)
 
 	// Output: Verbosity: [true true]
 	// Offset: 5
@@ -89,5 +98,7 @@
 	// StringSlice: [hello world]
 	// PtrSlice: [hello world]
 	// IntMap: [a:1 b:5]
-	// Remaining args: arg1 arg2 arg3
+	// Args.Id: id
+	// Args.Num: 10
+	// Args.Rest: [remaining1 remaining2]
 }
diff --git a/flags.go b/flags.go
index 13b0ee3..be007a5 100644
--- a/flags.go
+++ b/flags.go
@@ -107,6 +107,14 @@
 //                           specified name as an alias for the command. Can be
 //                           be specified multiple times to add more than one
 //                           alias (optional)
+//     positional-args:      when specified on a field with a struct type,
+//                           uses the fields of that struct to parse remaining
+//                           positional command line arguments into (in order
+//                           of the fields). If a field has a slice type,
+//                           then all remaining arguments will be added to it.
+//                           Positional arguments are optional by default,
+//                           unless the "required" tag is specified together
+//                           with the "positional-args" tag (optional)
 //
 // Either short: or long: must be specified to make the field eligible as an
 // option.
diff --git a/help.go b/help.go
index 8283223..0139215 100644
--- a/help.go
+++ b/help.go
@@ -22,6 +22,41 @@
 	indent          bool
 }
 
+const (
+	paddingBeforeOption                 = 2
+	distanceBetweenOptionAndDescription = 2
+)
+
+func (a *alignmentInfo) descriptionStart() int {
+	ret := a.maxLongLen + distanceBetweenOptionAndDescription
+
+	if a.hasShort {
+		ret += 2
+	}
+
+	if a.maxLongLen > 0 {
+		ret += 4
+	}
+
+	if a.hasValueName {
+		ret += 3
+	}
+
+	return ret
+}
+
+func (a *alignmentInfo) updateLen(name string, indent bool) {
+	l := utf8.RuneCountInString(name)
+
+	if indent {
+		l = l + 4
+	}
+
+	if l > a.maxLongLen {
+		a.maxLongLen = l
+	}
+}
+
 func (p *Parser) getAlignmentInfo() alignmentInfo {
 	ret := alignmentInfo{
 		maxLongLen:      0,
@@ -34,7 +69,15 @@
 		ret.terminalColumns = 80
 	}
 
+	var prevcmd *Command
+
 	p.eachActiveGroup(func(c *Command, grp *Group) {
+		if c != prevcmd {
+			for _, arg := range c.args {
+				ret.updateLen(arg.Name, c != p.Command)
+			}
+		}
+
 		for _, info := range grp.options {
 			if !info.canCli() {
 				continue
@@ -44,22 +87,11 @@
 				ret.hasShort = true
 			}
 
-			lv := utf8.RuneCountInString(info.ValueName)
-
-			if lv != 0 {
+			if len(info.ValueName) > 0 {
 				ret.hasValueName = true
 			}
 
-			l := utf8.RuneCountInString(info.LongNameWithNamespace()) + lv
-
-			if c != p.Command {
-				// for indenting
-				l = l + 4
-			}
-
-			if l > ret.maxLongLen {
-				ret.maxLongLen = l
-			}
+			ret.updateLen(info.LongNameWithNamespace()+info.ValueName, c != p.Command)
 		}
 	})
 
@@ -69,9 +101,6 @@
 func (p *Parser) writeHelpOption(writer *bufio.Writer, option *Option, info alignmentInfo) {
 	line := &bytes.Buffer{}
 
-	distanceBetweenOptionAndDescription := 2
-	paddingBeforeOption := 2
-
 	prefix := paddingBeforeOption
 
 	if info.indent {
@@ -87,19 +116,7 @@
 		line.WriteString("  ")
 	}
 
-	descstart := info.maxLongLen + paddingBeforeOption + distanceBetweenOptionAndDescription
-
-	if info.hasShort {
-		descstart += 2
-	}
-
-	if info.maxLongLen > 0 {
-		descstart += 4
-	}
-
-	if info.hasValueName {
-		descstart += 3
-	}
+	descstart := info.descriptionStart() + paddingBeforeOption
 
 	if len(option.LongName) > 0 {
 		if option.ShortName != 0 {
@@ -236,6 +253,28 @@
 				fmt.Fprintf(wr, " %s", allcmd.Name)
 			}
 
+			if len(allcmd.args) > 0 {
+				fmt.Fprintf(wr, " ")
+			}
+
+			for i, arg := range allcmd.args {
+				if i != 0 {
+					fmt.Fprintf(wr, " ")
+				}
+
+				name := arg.Name
+
+				if arg.isRemaining() {
+					name = name + "..."
+				}
+
+				if !allcmd.ArgsRequired {
+					fmt.Fprintf(wr, "[%s]", name)
+				} else {
+					fmt.Fprintf(wr, "%s", name)
+				}
+			}
+
 			if allcmd.Active == nil && len(allcmd.commands) > 0 {
 				var co, cc string
 
@@ -275,26 +314,32 @@
 		}
 	}
 
-	prevcmd := p.Command
+	c := p.Command
 
-	p.eachActiveGroup(func(c *Command, grp *Group) {
-		first := true
+	for c != nil {
+		printcmd := c != p.Command
 
-		// Skip built-in help group for all commands except the top-level
-		// parser
-		if grp.isBuiltinHelp && c != p.Command {
-			return
-		}
+		c.eachGroup(func(grp *Group) {
+			first := true
 
-		for _, info := range grp.options {
-			if info.canCli() {
-				if prevcmd != c {
-					fmt.Fprintf(wr, "\n[%s command options]\n", c.Name)
-					prevcmd = c
-					aligninfo.indent = true
+			// Skip built-in help group for all commands except the top-level
+			// parser
+			if grp.isBuiltinHelp && c != p.Command {
+				return
+			}
+
+			for _, info := range grp.options {
+				if !info.canCli() {
+					continue
 				}
 
-				if first && prevcmd.Group != grp {
+				if printcmd {
+					fmt.Fprintf(wr, "\n[%s command options]\n", c.Name)
+					aligninfo.indent = true
+					printcmd = false
+				}
+
+				if first && cmd.Group != grp {
 					fmt.Fprintln(wr)
 
 					if aligninfo.indent {
@@ -307,8 +352,32 @@
 
 				p.writeHelpOption(wr, info, aligninfo)
 			}
+		})
+
+		if len(c.args) > 0 {
+			if c == p.Command {
+				fmt.Fprintf(wr, "\nArguments:\n")
+			} else {
+				fmt.Fprintf(wr, "\n[%s command arguments]\n", c.Name)
+			}
+
+			maxlen := aligninfo.descriptionStart()
+
+			for _, arg := range c.args {
+				prefix := strings.Repeat(" ", paddingBeforeOption)
+				fmt.Fprintf(wr, "%s%s", prefix, arg.Name)
+
+				if len(arg.Description) > 0 {
+					align := strings.Repeat(" ", maxlen-len(arg.Name)-1)
+					fmt.Fprintf(wr, ":%s%s", align, arg.Description)
+				}
+
+				fmt.Fprintln(wr)
+			}
 		}
-	})
+
+		c = c.Active
+	}
 
 	scommands := cmd.sortedCommands()
 
diff --git a/help_test.go b/help_test.go
index a59cfbf..27de672 100644
--- a/help_test.go
+++ b/help_test.go
@@ -73,6 +73,11 @@
 	Command struct {
 		ExtraVerbose []bool `long:"extra-verbose" description:"Use for extra verbosity"`
 	} `command:"command" alias:"cm" alias:"cmd" description:"A command"`
+
+	Args struct {
+		Filename string `name:"filename" description:"A filename"`
+		Num      int    `name:"num" description:"A number"`
+	} `positional-args:"yes"`
 }
 
 func TestHelp(t *testing.T) {
@@ -98,7 +103,7 @@
 
 		if runtime.GOOS == "windows" {
 			expected = `Usage:
-  TestHelp [OPTIONS] <command>
+  TestHelp [OPTIONS] [filename] [num] <command>
 
 Application Options:
   /v, /verbose             Show verbose debug information
@@ -123,12 +128,16 @@
   /?                       Show this help message
   /h, /help                Show this help message
 
+Arguments:
+  filename:                A filename
+  num:                     A number
+
 Available commands:
   command  A command (aliases: cm, cmd)
 `
 		} else {
 			expected = `Usage:
-  TestHelp [OPTIONS] <command>
+  TestHelp [OPTIONS] [filename] [num] <command>
 
 Application Options:
   -v, --verbose            Show verbose debug information
@@ -152,6 +161,10 @@
 Help Options:
   -h, --help               Show this help message
 
+Arguments:
+  filename:                A filename
+  num:                     A number
+
 Available commands:
   command  A command (aliases: cm, cmd)
 `
diff --git a/ini_test.go b/ini_test.go
index 6d037bf..eb752e9 100644
--- a/ini_test.go
+++ b/ini_test.go
@@ -15,7 +15,7 @@
 		expected string
 	}{
 		{
-			[]string{"-vv", "--intmap=a:2", "--intmap", "b:3", "command"},
+			[]string{"-vv", "--intmap=a:2", "--intmap", "b:3", "filename", "0", "command"},
 			IniDefault,
 			`[Application Options]
 ; Show verbose debug information
@@ -30,7 +30,7 @@
 `,
 		},
 		{
-			[]string{"-vv", "--intmap=a:2", "--intmap", "b:3", "command"},
+			[]string{"-vv", "--intmap=a:2", "--intmap", "b:3", "filename", "0", "command"},
 			IniDefault | IniIncludeDefaults,
 			`[Application Options]
 ; Show verbose debug information
@@ -80,7 +80,7 @@
 `,
 		},
 		{
-			[]string{"command"},
+			[]string{"filename", "0", "command"},
 			IniDefault | IniIncludeDefaults | IniCommentDefaults,
 			`[Application Options]
 ; Show verbose debug information
@@ -128,7 +128,7 @@
 `,
 		},
 		{
-			[]string{"--default=New value", "--default-array=New value", "--default-map=new:value", "command"},
+			[]string{"--default=New value", "--default-array=New value", "--default-map=new:value", "filename", "0", "command"},
 			IniDefault | IniIncludeDefaults | IniCommentDefaults,
 			`[Application Options]
 ; Show verbose debug information
diff --git a/parser.go b/parser.go
index 4ebf1aa..aa93450 100644
--- a/parser.go
+++ b/parser.go
@@ -146,11 +146,15 @@
 		p.addHelpGroups(p.showBuiltinHelp)
 	}
 
+	positional := make([]*Arg, len(p.args))
+	copy(positional, p.args)
+
 	s := &parseState{
-		args:    args,
-		retargs: make([]string, 0, len(args)),
-		command: p.Command,
-		lookup:  p.makeLookup(),
+		args:       args,
+		retargs:    make([]string, 0, len(args)),
+		positional: positional,
+		command:    p.Command,
+		lookup:     p.makeLookup(),
 	}
 
 	for !s.eof() {
@@ -159,7 +163,7 @@
 		// When PassDoubleDash is set and we encounter a --, then
 		// simply append all the rest as arguments and break out
 		if (p.Options&PassDoubleDash) != None && arg == "--" {
-			s.retargs = append(s.retargs, s.args...)
+			s.addArgs(s.args...)
 			break
 		}
 
@@ -194,7 +198,7 @@
 			}
 
 			if ignoreUnknown {
-				s.retargs = append(s.retargs, arg)
+				s.addArgs(arg)
 			}
 		}
 	}
diff --git a/parser_private.go b/parser_private.go
index df640d8..a6ba1ae 100644
--- a/parser_private.go
+++ b/parser_private.go
@@ -10,10 +10,11 @@
 )
 
 type parseState struct {
-	arg     string
-	args    []string
-	retargs []string
-	err     error
+	arg        string
+	args       []string
+	retargs    []string
+	positional []*Arg
+	err        error
 
 	command *Command
 	lookup  lookup
@@ -60,6 +61,34 @@
 	}
 
 	if len(required) == 0 {
+		if len(p.positional) > 0 && p.command.ArgsRequired {
+			reqnames := make([]string, 0)
+
+			for _, arg := range p.positional {
+				if arg.isRemaining() {
+					break
+				}
+
+				reqnames = append(reqnames, "`"+arg.Name+"`")
+			}
+
+			if len(reqnames) == 0 {
+				return nil
+			}
+
+			var msg string
+
+			if len(reqnames) == 1 {
+				msg = fmt.Sprintf("the required argument %s was not provided", reqnames[0])
+			} else {
+				msg = fmt.Sprintf("the required arguments %s and %s were not provided",
+					strings.Join(reqnames[:len(reqnames)-1], ", "), reqnames[len(reqnames)-1])
+			}
+
+			p.err = newError(ErrRequired, msg)
+			return p.err
+		}
+
 		return nil
 	}
 
@@ -226,19 +255,52 @@
 	return nil
 }
 
+func (s *parseState) addArgs(args ...string) error {
+	for len(s.positional) > 0 && len(args) > 0 {
+		arg := s.positional[0]
+
+		if err := convert(args[0], arg.value, arg.tag); err != nil {
+			return err
+		}
+
+		if !arg.isRemaining() {
+			s.positional = s.positional[1:]
+		}
+
+		args = args[1:]
+	}
+
+	s.retargs = append(s.retargs, args...)
+	return nil
+}
+
 func (p *Parser) parseNonOption(s *parseState) error {
+	if len(s.positional) > 0 {
+		return s.addArgs(s.arg)
+	}
+
 	if cmd := s.lookup.commands[s.arg]; cmd != nil {
 		s.command.Active = cmd
 
 		s.command = cmd
 		s.lookup = cmd.makeLookup()
+
+		s.positional = make([]*Arg, len(cmd.args))
+		copy(s.positional, cmd.args)
 	} else if (p.Options & PassAfterNonOption) != None {
 		// If PassAfterNonOption is set then all remaining arguments
 		// are considered positional
-		s.retargs = append(append(s.retargs, s.arg), s.args...)
+		if err := s.addArgs(s.arg); err != nil {
+			return err
+		}
+
+		if err := s.addArgs(s.args...); err != nil {
+			return err
+		}
+
 		s.args = []string{}
 	} else {
-		s.retargs = append(s.retargs, s.arg)
+		return s.addArgs(s.arg)
 	}
 
 	return nil