Parse command line arguments.

def parse_args(args=None):
    """Parse command line arguments."""
    # The argparse module provides a nice abstraction for argument parsing.
    # It automatically builds up the help text, too.
    parser = argparse.ArgumentParser(
            prog=__package__,
            description='Make substitutions in text files.',
        )
    parser.add_argument('-V', '--version',
                        action='version',
                        version="%%(prog)s %s" % (__version__,),
                        )

    group = parser.add_argument_group("Engine")
    group.add_argument('-e', '--engine',
                       dest='engine',
                       default='string.Template',
                       help="templating engine",
                       metavar="ENGINE",
                       )
    group.add_argument('-t', '--tolerant',
                       action='store_true',
                       dest='tolerant',
                       help="don't fail on missing names",
                       )

    group = parser.add_argument_group("Output")
    group.add_argument('-s', '--stdout',
                       action='append_const',
                       dest='outfiles',
                       const=sys.stdout,
                       help="use standard output",
                       )
    group.add_argument('-o', '--outfile',
                       action='append',
                       dest='outfiles',
                       help="output file",
                       metavar="FILE",
                       )
    group.add_argument('--vary',
                       action='store_true',
                       dest='vary',
                       help="vary output file name according to template",
                       )
    group.add_argument('-r', '--read-old',
                       action='store_true',
                       dest='read_old',
                       help="read preexisting output files and"
                            "hand the respective content to the template",
                       )
    group.add_argument('-d', '--delete-empty',
                       action='store_true',
                       dest='delete_empty',
                       help="delete file if output is empty",
                       )

    group = parser.add_argument_group("Input")
    group.add_argument('--stdin',
                       action='append_const',
                       dest='infiles',
                       const=sys.stdin,
                       help="use standard input",
                       )
    group.add_argument('-i', '--infile',
                       action='append',
                       dest='infiles',
                       help="any number of input files",
                       metavar="FILE",
                       )
    group.add_argument('-c', '--concatenate',
                       action='store_true',
                       dest='concatenate',
                       help="concatenate multiple input files into one output",
                       )

    group = parser.add_argument_group("Name-value pairs")
    group.add_argument('-a', '--arg',
                       action='append',
                       dest='args',
                       help="any number of name-value pairs",
                       metavar="NAME=VALUE",
                       )
    group.add_argument('-p', '--pyarg',
                       action='append',
                       dest='args',
                       type=_PyArg,
                       help="evaluate a python expression",
                       metavar="NAME=EXPRESSION",
                       )
    group.add_argument('-n', '--next',
                       action='append_const',
                       dest='args',
                       const='--',
                       help="begin next argument group",
                       )

    parser.add_argument(
                        dest='remainder',
                        nargs=argparse.REMAINDER,
                        help="possible input files and name-value pair groups "
                             "if not already specified through options",
                        )

    args = parser.parse_args(args)

    if args.engine == 'help':
        dump_engines()
        parser.exit(0)

    if args.engine not in engines.engines:
        parser.error("Engine '%s' is not available." % (args.engine,))

    if args.vary:
        if len(args.outfiles) != 1:
            parser.error("need exactly one output file template")
        if is_filelike(args.outfiles[0]):
            parser.error("vary requires an output file template")
    elif not args.outfiles:
        args.outfiles = [sys.stdout]

    if not args.infiles:
        if args.args:
            infiles = args.remainder
            args.remainder = []
            try:
                infiles.remove('--')
            except ValueError:
                pass
        else:
            first = 1 if args.remainder and args.remainder[0] == '--' else 0
            last = (len(args.remainder)
                    if args.vary or args.concatenate
                    else first + 1)
            for split, infile in enumerate(args.remainder[first:last], first):
                if infile == '--' or '=' in infile:
                    break
            else:
                split = last

            infiles = args.remainder[first:split]
            args.remainder = args.remainder[split:]

        args.infiles = [path if path != '-' else sys.stdin
                        for path in infiles] if infiles else [sys.stdin]

    if args.args:
        flat_args = args.args
    else:
        flat_args = args.remainder
        args.remainder = []
        if flat_args and flat_args[0] == '--':
            flat_args = flat_args[1:]

    args.args = []
    mapping = {}
    for arg in flat_args:
        if isinstance(arg, _PyArg):
            name_value = arg.split('=', 1)
            mapping[name_value[0]] = eval(name_value[1], {}, mapping)
        elif arg == '--':
            args.args.append(mapping)
            mapping = {}
        else:
            name_value = arg.split('=', 1)
            mapping[name_value[0]] = (name_value[1]
                                      if len(name_value) > 1
                                      else None)
    args.args.append(mapping)

    if args.remainder:
        parser.error("extraneous arguments left over")
    else:
        del args.remainder

    return args