Add --undefok support

Some other arg parsing libraries support an `--undefok` option which
suppresses the error that would normally surface if an unknown flag or
option is passed.
For example: https://gflags.github.io/gflags/#special

Add built in support, unconditionally, for `--undefok` in the parser.
The `undefok` option always supports multiple values and comma separated
values.

If an option named `undefok` is added to the `ArgParser` it will
override this behavior. It isn't possible to implement this behavior in
the calling code because the `callback` for when an option is parsed is
not triggered until after all arguments are parsed. Allowing the calling
code to override the `undefok` option, even though it can't implement
the expected semantics, retains backwards compatibility in the unlikely
case there are existing uses of that name.

- Add an `_undefok` field to `Parser` to track the names that suppressed
  as options are parsed. Only the top level parser in a `parent` chain
  will handle `undefok`.
- When parsing an otherwise unknown option named `undefok`, parse the
  value and store in the allowed `_undefok` set.
- When parsing an otherwise unknown option with any other name already
  in `_undefok`, silently drop it, along with any `=value` portion.

This implementation has the following limitations:
- The `--undefok` option must be passed _before_ the unknown option. If
  the arguments come in the other order it will still fail.
- The unknown option must either be a flag, or must use the
  `--name=value` syntax. If it is passed as `--name value` then the
  value will show in `argResults.rest`. This could cause the argument to
  get misinterpreted by the app.
diff --git a/lib/src/parser.dart b/lib/src/parser.dart
index 25497be..02c0dd4 100644
--- a/lib/src/parser.dart
+++ b/lib/src/parser.dart
@@ -34,6 +34,8 @@
   /// The accumulated parsed options.
   final Map<String, dynamic> results = <String, dynamic>{};
 
+  final _undefok = <String>{};
+
   Parser(this.commandName, this.grammar, this.args,
       [this.parent, List<String>? rest]) {
     if (rest != null) this.rest.addAll(rest);
@@ -66,7 +68,8 @@
       if (command != null) {
         validate(rest.isEmpty, 'Cannot specify arguments before a command.');
         var commandName = args.removeFirst();
-        var commandParser = Parser(commandName, command, args, this, rest);
+        var commandParser =
+            Parser(commandName, command, args, this, rest);
 
         try {
           commandResults = commandParser.parse();
@@ -284,8 +287,24 @@
       setFlag(results, option, false);
     } else {
       // Walk up to the parent command if possible.
-      validate(parent != null, 'Could not find an option named "$name".');
-      return parent!.parseLongOption();
+      if (parent != null) {
+        return parent!.parseLongOption();
+      }
+      if (name == 'undefok') {
+        args.removeFirst();
+        if (value == null) {
+          validate(args.isNotEmpty, 'Missing argument for "--undefok".');
+          value = current;
+          args.removeFirst();
+        }
+        _undefok.addAll(value.split(','));
+        return true;
+      }
+      if (_undefok.contains(name)) {
+        args.removeFirst();
+        return true;
+      }
+      validate(false, 'Could not find an option named "$name".');
     }
 
     return true;