Using argparse module to parse the cmd line
authoryyh <yyh@123.com>
Tue, 26 Jan 2016 10:21:34 +0000 (18:21 +0800)
committeryyh <yyh@123.com>
Thu, 4 Feb 2016 06:18:57 +0000 (14:18 +0800)
Conflicts:
plugins/imager/loop_plugin.py

mic/cmd_chroot.py [new file with mode: 0755]
mic/cmd_create.py [new file with mode: 0755]
mic/creator.py [deleted file]
mic/helpformat.py [new file with mode: 0755]
mic/utils/cmdln.py [deleted file]
packaging/mic.spec
plugins/imager/fs_plugin.py
plugins/imager/loop_plugin.py
plugins/imager/qcow_plugin.py
tools/mic

diff --git a/mic/cmd_chroot.py b/mic/cmd_chroot.py
new file mode 100755 (executable)
index 0000000..d7d76ef
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/python -tt\r
+# vim: ai ts=4 sts=4 et sw=4\r
+#\r
+# Copyright (c) 2012 Intel, Inc.\r
+#\r
+# This program is free software; you can redistribute it and/or modify it\r
+# under the terms of the GNU General Public License as published by the Free\r
+# Software Foundation; version 2 of the License\r
+#\r
+# This program is distributed in the hope that it will be useful, but\r
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
+# for more details.\r
+#\r
+# You should have received a copy of the GNU General Public License along\r
+# with this program; if not, write to the Free Software Foundation, Inc., 59\r
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+\r
+"""Implementation of subcmd: chroot\r
+"""\r
+\r
+import os\r
+import os, sys, re\r
+import pwd\r
+import argparse\r
+\r
+from mic import msger\r
+from mic.utils import misc, errors\r
+from mic.conf import configmgr\r
+from mic.plugin import pluginmgr\r
+\r
+def _root_confirm():\r
+    """Make sure command is called by root\r
+    There are a lot of commands needed to be run during creating images,\r
+    some of them must be run with root privilege like mount, kpartx"""\r
+    if os.geteuid() != 0:\r
+        msger.error('Root permission is required to continue, abort')\r
+            \r
+def main(parser, args, argv):\r
+    """mic choot entry point."""\r
+\r
+    #args is argparser namespace, argv is the input cmd line\r
+    if args is None:\r
+        raise errors.Usage("Invalid arguments")\r
+\r
+    targetimage = args.imagefile\r
+    if not os.path.exists(targetimage):\r
+        raise errors.CreatorError("Cannot find the image: %s"\r
+                                  % targetimage)\r
+\r
+    _root_confirm()\r
+\r
+    configmgr.chroot['saveto'] = args.saveto\r
+\r
+    imagetype = misc.get_image_type(targetimage)\r
+    if imagetype in ("ext3fsimg", "ext4fsimg", "btrfsimg"):\r
+        imagetype = "loop"\r
+\r
+    chrootclass = None\r
+    for pname, pcls in pluginmgr.get_plugins('imager').iteritems():\r
+        if pname == imagetype and hasattr(pcls, "do_chroot"):\r
+            chrootclass = pcls\r
+            break\r
+\r
+    if not chrootclass:\r
+        raise errors.CreatorError("Cannot support image type: %s" \\r
+                                  % imagetype)\r
+\r
+    chrootclass.do_chroot(targetimage, args.cmd)\r
+        \r
+    \r
diff --git a/mic/cmd_create.py b/mic/cmd_create.py
new file mode 100755 (executable)
index 0000000..ce574a1
--- /dev/null
@@ -0,0 +1,234 @@
+#!/usr/bin/python -tt\r
+# vim: ai ts=4 sts=4 et sw=4\r
+#\r
+# Copyright (c) 2012 Intel, Inc.\r
+#\r
+# This program is free software; you can redistribute it and/or modify it\r
+# under the terms of the GNU General Public License as published by the Free\r
+# Software Foundation; version 2 of the License\r
+#\r
+# This program is distributed in the hope that it will be useful, but\r
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
+# for more details.\r
+#\r
+# You should have received a copy of the GNU General Public License along\r
+# with this program; if not, write to the Free Software Foundation, Inc., 59\r
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+\r
+"""Implementation of subcmd: create\r
+"""\r
+\r
+import os\r
+import os, sys, re\r
+import pwd\r
+import argparse\r
+\r
+from mic import msger\r
+from mic.utils import errors, rpmmisc\r
+from mic.conf import configmgr\r
+from mic.plugin import pluginmgr\r
+\r
+def main(parser, args, argv):\r
+    """mic create entry point."""\r
+    #args is argparser namespace, argv is the input cmd line\r
+    if args is None:\r
+        raise errors.Usage("Invalid arguments")\r
+\r
+    if not os.path.exists(args.ksfile):\r
+        raise errors.CreatorError("Can't find the file: %s" % args.ksfile)\r
+\r
+    if os.geteuid() != 0:\r
+        msger.error("Root permission is required, abort")\r
+        \r
+    try:\r
+        w = pwd.getpwuid(os.geteuid())\r
+    except KeyError:\r
+        msger.warning("Might fail in compressing stage for undetermined user")\r
+    \r
+    abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))\r
+    if args.logfile:\r
+        logfile_abs_path = abspath(args.logfile)\r
+        if os.path.isdir(logfile_abs_path):\r
+            raise errors.Usage("logfile's path %s should be file"\r
+                               % args.logfile)\r
+        configmgr.create['logfile'] = logfile_abs_path\r
+        configmgr.set_logfile()\r
+    \r
+    if args.subcommand == "auto":\r
+        do_auto(parser, args.ksfile, argv)\r
+        return\r
+        \r
+    #check the imager type\r
+    createrClass = None\r
+    for subcmd, klass in pluginmgr.get_plugins('imager').iteritems():\r
+        if subcmd == args.subcommand and hasattr(klass, 'do_create'):\r
+            createrClass = klass\r
+\r
+    if createrClass is None:\r
+        raise errors.CreatorError("Can't support subcommand %s" % args.subcommand)\r
+\r
+    if args.config:\r
+        configmgr.reset()\r
+        configmgr._siteconf = args.config\r
+\r
+    if args.outdir is not None:\r
+        configmgr.create['outdir'] = abspath(args.outdir)\r
+    if args.cachedir is not None:\r
+        configmgr.create['cachedir'] = abspath(args.cachedir)\r
+    os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir']\r
+\r
+    for cdir in ('outdir', 'cachedir'):\r
+        if os.path.exists(configmgr.create[cdir]) \\r
+          and not os.path.isdir(configmgr.create[cdir]):\r
+            raise errors.Usage('Invalid directory specified: %s' \\r
+                               % configmgr.create[cdir])\r
+        if not os.path.exists(configmgr.create[cdir]):\r
+            os.makedirs(configmgr.create[cdir])\r
+            if os.getenv('SUDO_UID', '') and os.getenv('SUDO_GID', ''):\r
+                os.chown(configmgr.create[cdir],\r
+                         int(os.getenv('SUDO_UID')),\r
+                         int(os.getenv('SUDO_GID')))\r
+\r
+    if args.local_pkgs_path is not None:\r
+        if not os.path.exists(args.local_pkgs_path):\r
+            raise errors.Usage('Local pkgs directory: \'%s\' not exist' \\r
+                          % args.local_pkgs_path)\r
+        configmgr.create['local_pkgs_path'] = args.local_pkgs_path\r
+\r
+    if args.release:\r
+        configmgr.create['release'] = args.release.rstrip('/')\r
+\r
+    if args.record_pkgs:\r
+        configmgr.create['record_pkgs'] = []\r
+        for infotype in args.record_pkgs.split(','):\r
+            if infotype not in ('name', 'content', 'license', 'vcs'):\r
+                raise errors.Usage('Invalid pkg recording: %s, valid ones:'\r
+                                   ' "name", "content", "license", "vcs"' \\r
+                                   % infotype)\r
+\r
+            configmgr.create['record_pkgs'].append(infotype)\r
+\r
+    if args.strict_mode:\r
+      configmgr.create['strict_mode'] = args.strict_mode\r
+    if args.arch is not None:\r
+        supported_arch = sorted(rpmmisc.archPolicies.keys(), reverse=True)\r
+        if args.arch in supported_arch:\r
+            configmgr.create['arch'] = args.arch\r
+        else:\r
+            raise errors.Usage('Invalid architecture: "%s".\n'\r
+                               '  Supported architectures are: \n'\r
+                               '  %s' % (args.arch,\r
+                                           ', '.join(supported_arch)))\r
+\r
+    if args.pkgmgr is not None:\r
+        configmgr.create['pkgmgr'] = args.pkgmgr\r
+\r
+    if args.runtime:\r
+        configmgr.set_runtime(args.runtime)\r
+\r
+    if args.pack_to is not None:\r
+        configmgr.create['pack_to'] = args.pack_to\r
+\r
+    if args.copy_kernel:\r
+        configmgr.create['copy_kernel'] = args.copy_kernel\r
+\r
+    if args.install_pkgs:\r
+        configmgr.create['install_pkgs'] = []\r
+        for pkgtype in args.install_pkgs.split(','):\r
+            if pkgtype not in ('source', 'debuginfo', 'debugsource'):\r
+                raise errors.Usage('Invalid parameter specified: "%s", '\r
+                                   'valid values: source, debuginfo, '\r
+                                   'debusource' % pkgtype)\r
+\r
+            configmgr.create['install_pkgs'].append(pkgtype)\r
+\r
+    if args.check_pkgs:\r
+        for pkg in args.check_pkgs.split(','):\r
+            configmgr.create['check_pkgs'].append(pkg)\r
+\r
+    if args.enabletmpfs:\r
+        configmgr.create['enabletmpfs'] = args.enabletmpfs\r
+\r
+    if args.repourl:\r
+        for item in args.repourl:\r
+            try:\r
+                key, val = item.split('=')\r
+            except:\r
+                continue\r
+            configmgr.create['repourl'][key] = val\r
+\r
+    if args.repo:\r
+        for optvalue in args.repo:\r
+            repo = {}\r
+            for item in optvalue.split(';'):\r
+                try:\r
+                    key, val = item.split('=')\r
+                except:\r
+                    continue\r
+                repo[key.strip()] = val.strip()\r
+            if 'name' in repo:\r
+                configmgr.create['extrarepos'][repo['name']] = repo\r
+\r
+    if args.ignore_ksrepo:\r
+        configmgr.create['ignore_ksrepo'] = args.ignore_ksrepo\r
+        \r
+    creater = createrClass()\r
+    creater.do_create(args)\r
+\r
+def do_auto(parser, ksfile, argv):\r
+        """${cmd_name}: auto detect image type from magic header\r
+\r
+        Usage:\r
+            ${name} ${cmd_name} <ksfile>\r
+\r
+        ${cmd_option_list}\r
+        """\r
+        def parse_magic_line(re_str, pstr, ptype='mic'):\r
+            ptn = re.compile(re_str)\r
+            m = ptn.match(pstr)\r
+            if not m or not m.groups():\r
+                return None\r
+\r
+            inline_argv = m.group(1).strip()\r
+            if ptype == 'mic':\r
+                m2 = re.search('(?P<format>\w+)', inline_argv)\r
+            elif ptype == 'mic2':\r
+                m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)',\r
+                               inline_argv)\r
+            else:\r
+                return None\r
+\r
+            if m2:\r
+                cmdname = m2.group('format')\r
+                inline_argv = inline_argv.replace(m2.group(0), '')\r
+                return (cmdname, inline_argv)\r
+\r
+            return None\r
+\r
+        if not os.path.exists(ksfile):\r
+            raise errors.CreatorError("Can't find the file: %s" % ksfile)\r
+\r
+        with open(ksfile, 'r') as rf:\r
+            first_line = rf.readline()\r
+\r
+        mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-'\r
+        mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-'\r
+\r
+        result = parse_magic_line(mic_re, first_line, 'mic') \\r
+                 or parse_magic_line(mic2_re, first_line, 'mic2')\r
+        if not result:\r
+            raise errors.KsError("Invalid magic line in file: %s" % ksfile)\r
+        \r
+        ksargv = ' '.join(result).split()\r
+\r
+        argv.remove("auto")\r
+        index = argv.index("create")\r
+        #insert the subcommand\r
+        argv.insert(index+1, ksargv[0])\r
+        options = argv + ksargv[1:]\r
+\r
+        args = parser.parse_args(options)\r
+\r
+        main(parser, args, options)\r
+\r
diff --git a/mic/creator.py b/mic/creator.py
deleted file mode 100644 (file)
index e7c6633..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-#!/usr/bin/python -tt
-#
-# Copyright (c) 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os, sys, re
-import pwd
-from optparse import SUPPRESS_HELP
-
-from mic import msger
-from mic.utils import cmdln, errors, rpmmisc
-from mic.conf import configmgr
-from mic.plugin import pluginmgr
-
-
-class Creator(cmdln.Cmdln):
-    """${name}: create an image
-
-    Usage:
-        ${name} SUBCOMMAND <ksfile> [OPTS]
-
-    ${command_list}
-    ${option_list}
-    """
-
-    name = 'mic create(cr)'
-
-    def __init__(self, *args, **kwargs):
-        cmdln.Cmdln.__init__(self, *args, **kwargs)
-        self._subcmds = []
-
-        # get cmds from pluginmgr
-        # mix-in do_subcmd interface
-        for subcmd, klass in pluginmgr.get_plugins('imager').iteritems():
-            if not hasattr(klass, 'do_create'):
-                msger.warning("Unsurpport subcmd: %s" % subcmd)
-                continue
-
-            func = getattr(klass, 'do_create')
-            setattr(self.__class__, "do_"+subcmd, func)
-            self._subcmds.append(subcmd)
-
-    def get_optparser(self):
-        optparser = cmdln.CmdlnOptionParser(self)
-        optparser.add_option('-d', '--debug', action='store_true',
-                             dest='debug',
-                             help=SUPPRESS_HELP)
-        optparser.add_option('-v', '--verbose', action='store_true',
-                             dest='verbose',
-                             help=SUPPRESS_HELP)
-        optparser.add_option('', '--logfile', type='string', dest='logfile',
-                             default=None,
-                             help='Path of logfile')
-        optparser.add_option('-c', '--config', type='string', dest='config',
-                             default=None,
-                             help='Specify config file for mic')
-        optparser.add_option('-k', '--cachedir', type='string', action='store',
-                             dest='cachedir', default=None,
-                             help='Cache directory to store the downloaded')
-        optparser.add_option('-o', '--outdir', type='string', action='store',
-                             dest='outdir', default=None,
-                             help='Output directory')
-        optparser.add_option('-A', '--arch', type='string', dest='arch',
-                             default=None,
-                             help='Specify repo architecture')
-        optparser.add_option('', '--release', type='string', dest='release',
-                             default=None, metavar='RID',
-                             help='Generate a release of RID with all necessary'
-                                  ' files, when @BUILD_ID@ is contained in '
-                                  'kickstart file, it will be replaced by RID')
-        optparser.add_option("", "--record-pkgs", type="string",
-                             dest="record_pkgs", default=None,
-                             help='Record the info of installed packages, '
-                                  'multiple values can be specified which '
-                                  'joined by ",", valid values: "name", '
-                                  '"content", "license", "vcs"')
-        optparser.add_option('', '--pkgmgr', type='string', dest='pkgmgr',
-                             default=None,
-                             help='Specify backend package manager')
-        optparser.add_option('', '--local-pkgs-path', type='string',
-                             dest='local_pkgs_path', default=None,
-                             help='Path for local pkgs(rpms) to be installed')
-        optparser.add_option('', '--runtime', type='string',
-                             dest='runtime', default=None,
-                             help='Specify  runtime mode, avaiable: bootstrap')
-        # --taring-to is alias to --pack-to
-        optparser.add_option('', '--taring-to', type='string',
-                             dest='pack_to', default=None,
-                             help=SUPPRESS_HELP)
-        optparser.add_option('', '--pack-to', type='string',
-                             dest='pack_to', default=None,
-                             help='Pack the images together into the specified'
-                                  ' achive, extension supported: .zip, .tar, '
-                                  '.tar.gz, .tar.bz2, etc. by default, .tar '
-                                  'will be used')
-        optparser.add_option('', '--copy-kernel', action='store_true',
-                             dest='copy_kernel',
-                             help='Copy kernel files from image /boot directory'
-                                  ' to the image output directory.')
-        optparser.add_option('', '--install-pkgs', type='string', action='store',
-                             dest='install_pkgs', default=None,
-                             help='Specify what type of packages to be installed,'
-                                  ' valid: source, debuginfo, debugsource')
-        optparser.add_option('', '--check-pkgs', type='string', action='store',
-                             dest='check_pkgs', default=[],
-                             help='Check if given packages would be installed, '
-                                  'packages should be separated by comma')
-        optparser.add_option('', '--tmpfs', action='store_true', dest='enabletmpfs',
-                             help='Setup tmpdir as tmpfs to accelerate, experimental'
-                                  ' feature, use it if you have more than 4G memory')
-        optparser.add_option('', '--repourl', action='append',
-                             dest='repourl', default=[],
-                             help=SUPPRESS_HELP)
-        optparser.add_option('-R', '--repo', action='append',
-                             dest='repo', default=[],
-                             help=SUPPRESS_HELP)
-        optparser.add_option('', '--ignore-ksrepo', action='store_true',
-                             dest='ignore_ksrepo', default=False,
-                             help=SUPPRESS_HELP)
-        optparser.add_option('', '--strict-mode', action='store_true',
-                             dest='strict_mode', default=False,
-                             help='Abort creation of image, if there are some errors'
-                                  ' during rpm installation. ')
-        return optparser
-
-    def preoptparse(self, argv):
-        optparser = self.get_optparser()
-
-        largs = []
-        rargs = []
-        while argv:
-            arg = argv.pop(0)
-
-            if arg in ('-h', '--help'):
-                rargs.append(arg)
-
-            elif optparser.has_option(arg):
-                largs.append(arg)
-
-                if optparser.get_option(arg).takes_value():
-                    try:
-                        largs.append(argv.pop(0))
-                    except IndexError:
-                        raise errors.Usage("option %s requires arguments" % arg)
-
-            else:
-                if arg.startswith("--"):
-                    if "=" in arg:
-                        opt = arg.split("=")[0]
-                    else:
-                        opt = None
-                elif arg.startswith("-") and len(arg) > 2:
-                    opt = arg[0:2]
-                else:
-                    opt = None
-
-                if opt and optparser.has_option(opt):
-                    largs.append(arg)
-                else:
-                    rargs.append(arg)
-
-        return largs + rargs
-
-    def postoptparse(self):
-        abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))
-
-        if self.options.verbose:
-            msger.set_loglevel('VERBOSE')
-        if self.options.debug:
-            msger.set_loglevel('DEBUG')
-
-        if self.options.logfile:
-            logfile_abs_path = abspath(self.options.logfile)
-            if os.path.isdir(logfile_abs_path):
-                raise errors.Usage("logfile's path %s should be file"
-                                   % self.options.logfile)
-            configmgr.create['logfile'] = logfile_abs_path
-            configmgr.set_logfile()
-
-        if self.options.config:
-            configmgr.reset()
-            configmgr._siteconf = self.options.config
-
-        if self.options.outdir is not None:
-            configmgr.create['outdir'] = abspath(self.options.outdir)
-        if self.options.cachedir is not None:
-            configmgr.create['cachedir'] = abspath(self.options.cachedir)
-        os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir']
-
-        for cdir in ('outdir', 'cachedir'):
-            if os.path.exists(configmgr.create[cdir]) \
-              and not os.path.isdir(configmgr.create[cdir]):
-                raise errors.Usage('Invalid directory specified: %s' \
-                                   % configmgr.create[cdir])
-            if not os.path.exists(configmgr.create[cdir]):
-                os.makedirs(configmgr.create[cdir])
-                if os.getenv('SUDO_UID', '') and os.getenv('SUDO_GID', ''):
-                    os.chown(configmgr.create[cdir],
-                             int(os.getenv('SUDO_UID')),
-                             int(os.getenv('SUDO_GID')))
-
-        if self.options.local_pkgs_path is not None:
-            if not os.path.exists(self.options.local_pkgs_path):
-                raise errors.Usage('Local pkgs directory: \'%s\' not exist' \
-                              % self.options.local_pkgs_path)
-            configmgr.create['local_pkgs_path'] = self.options.local_pkgs_path
-
-        if self.options.release:
-            configmgr.create['release'] = self.options.release.rstrip('/')
-
-        if self.options.record_pkgs:
-            configmgr.create['record_pkgs'] = []
-            for infotype in self.options.record_pkgs.split(','):
-                if infotype not in ('name', 'content', 'license', 'vcs'):
-                    raise errors.Usage('Invalid pkg recording: %s, valid ones:'
-                                       ' "name", "content", "license", "vcs"' \
-                                       % infotype)
-
-                configmgr.create['record_pkgs'].append(infotype)
-
-        if self.options.strict_mode:
-          configmgr.create['strict_mode'] = self.options.strict_mode
-        if self.options.arch is not None:
-            supported_arch = sorted(rpmmisc.archPolicies.keys(), reverse=True)
-            if self.options.arch in supported_arch:
-                configmgr.create['arch'] = self.options.arch
-            else:
-                raise errors.Usage('Invalid architecture: "%s".\n'
-                                   '  Supported architectures are: \n'
-                                   '  %s' % (self.options.arch,
-                                               ', '.join(supported_arch)))
-
-        if self.options.pkgmgr is not None:
-            configmgr.create['pkgmgr'] = self.options.pkgmgr
-
-        if self.options.runtime:
-            configmgr.set_runtime(self.options.runtime)
-
-        if self.options.pack_to is not None:
-            configmgr.create['pack_to'] = self.options.pack_to
-
-        if self.options.copy_kernel:
-            configmgr.create['copy_kernel'] = self.options.copy_kernel
-
-        if self.options.install_pkgs:
-            configmgr.create['install_pkgs'] = []
-            for pkgtype in self.options.install_pkgs.split(','):
-                if pkgtype not in ('source', 'debuginfo', 'debugsource'):
-                    raise errors.Usage('Invalid parameter specified: "%s", '
-                                       'valid values: source, debuginfo, '
-                                       'debusource' % pkgtype)
-
-                configmgr.create['install_pkgs'].append(pkgtype)
-
-        if self.options.check_pkgs:
-            for pkg in self.options.check_pkgs.split(','):
-                configmgr.create['check_pkgs'].append(pkg)
-
-        if self.options.enabletmpfs:
-            configmgr.create['enabletmpfs'] = self.options.enabletmpfs
-
-        if self.options.repourl:
-            for item in self.options.repourl:
-                try:
-                    key, val = item.split('=')
-                except:
-                    continue
-                configmgr.create['repourl'][key] = val
-
-        if self.options.repo:
-            for optvalue in self.options.repo:
-                repo = {}
-                for item in optvalue.split(';'):
-                    try:
-                        key, val = item.split('=')
-                    except:
-                        continue
-                    repo[key.strip()] = val.strip()
-                if 'name' in repo:
-                    configmgr.create['extrarepos'][repo['name']] = repo
-
-        if self.options.ignore_ksrepo:
-            configmgr.create['ignore_ksrepo'] = self.options.ignore_ksrepo
-
-    def main(self, argv=None):
-        if argv is None:
-            argv = sys.argv
-        else:
-            argv = argv[:] # don't modify caller's list
-
-        self.optparser = self.get_optparser()
-        if self.optparser:
-            try:
-                argv = self.preoptparse(argv)
-                self.options, args = self.optparser.parse_args(argv)
-
-            except cmdln.CmdlnUserError, ex:
-                msg = "%s: %s\nTry '%s help' for info.\n"\
-                      % (self.name, ex, self.name)
-                raise errors.Usage(msg)
-
-            except cmdln.StopOptionProcessing, ex:
-                return 0
-        else:
-            # optparser=None means no process for opts
-            self.options, args = None, argv[1:]
-
-        if not args:
-            return self.emptyline()
-
-        self.postoptparse()
-
-        return self.cmd(args)
-
-    def precmd(self, argv): # check help before cmd
-
-        if '-h' in argv or '?' in argv or '--help' in argv or 'help' in argv:
-            return argv
-
-        if len(argv) == 1:
-            return ['help', argv[0]]
-
-        if os.geteuid() != 0:
-            msger.error("Root permission is required, abort")
-
-        try:
-            w = pwd.getpwuid(os.geteuid())
-        except KeyError:
-            msger.warning("Might fail in compressing stage for undetermined user")
-
-        return argv
-
-    def do_auto(self, subcmd, opts, *args):
-        """${cmd_name}: auto detect image type from magic header
-
-        Usage:
-            ${name} ${cmd_name} <ksfile>
-
-        ${cmd_option_list}
-        """
-        def parse_magic_line(re_str, pstr, ptype='mic'):
-            ptn = re.compile(re_str)
-            m = ptn.match(pstr)
-            if not m or not m.groups():
-                return None
-
-            inline_argv = m.group(1).strip()
-            if ptype == 'mic':
-                m2 = re.search('(?P<format>\w+)', inline_argv)
-            elif ptype == 'mic2':
-                m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)',
-                               inline_argv)
-            else:
-                return None
-
-            if m2:
-                cmdname = m2.group('format')
-                inline_argv = inline_argv.replace(m2.group(0), '')
-                return (cmdname, inline_argv)
-
-            return None
-
-        if len(args) != 1:
-            raise errors.Usage("Extra arguments given")
-
-        if not os.path.exists(args[0]):
-            raise errors.CreatorError("Can't find the file: %s" % args[0])
-
-        with open(args[0], 'r') as rf:
-            first_line = rf.readline()
-
-        mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-'
-        mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-'
-
-        result = parse_magic_line(mic_re, first_line, 'mic') \
-                 or parse_magic_line(mic2_re, first_line, 'mic2')
-        if not result:
-            raise errors.KsError("Invalid magic line in file: %s" % args[0])
-
-        if result[0] not in self._subcmds:
-            raise errors.KsError("Unsupport format '%s' in %s"
-                                 % (result[0], args[0]))
-
-        argv = ' '.join(result + args).split()
-        self.main(argv)
-
diff --git a/mic/helpformat.py b/mic/helpformat.py
new file mode 100755 (executable)
index 0000000..e17f716
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/env python\r
+# vim: ai ts=4 sts=4 et sw=4\r
+#\r
+# Copyright (c) 2011 Intel, Inc.\r
+#\r
+# This program is free software; you can redistribute it and/or modify it\r
+# under the terms of the GNU General Public License as published by the Free\r
+# Software Foundation; version 2 of the License\r
+#\r
+# This program is distributed in the hope that it will be useful, but\r
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
+# for more details.\r
+#\r
+# You should have received a copy of the GNU General Public License along\r
+# with this program; if not, write to the Free Software Foundation, Inc., 59\r
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+\r
+"""Local additions to commandline parsing."""\r
+\r
+import os\r
+import re\r
+import functools\r
+\r
+from argparse import RawDescriptionHelpFormatter, ArgumentTypeError\r
+\r
+class MICHelpFormatter(RawDescriptionHelpFormatter):\r
+    """Changed default argparse help output by request from cmdln lovers."""\r
+\r
+    def __init__(self, *args, **kwargs):\r
+        super(MICHelpFormatter, self).__init__(*args, **kwargs)\r
+        self._aliases = {}\r
+\r
+    def add_argument(self, action):\r
+        """Collect aliases."""\r
+\r
+        if action.choices:\r
+            for item, parser in action.choices.iteritems():\r
+                self._aliases[str(item)] = parser.get_default('alias')\r
+\r
+        return super(MICHelpFormatter, self).add_argument(action)\r
+\r
+    def format_help(self):\r
+        """\r
+        There is no safe and documented way in argparse to reformat\r
+        help output through APIs as almost all of them are private,\r
+        so this method just parses the output and changes it.\r
+        """\r
+        result = []\r
+        subcomm = False\r
+        for line in super(MICHelpFormatter, self).format_help().split('\n'):\r
+            if line.strip().startswith('{'):\r
+                continue\r
+            if line.startswith('optional arguments:'):\r
+                line = 'Global Options:'\r
+            if line.startswith('usage:'):\r
+                line = "Usage: mic [GLOBAL-OPTS] SUBCOMMAND [OPTS]"\r
+            if subcomm:\r
+                match = re.match("[ ]+([^ ]+)[ ]+(.+)", line)\r
+                if match:\r
+                    name, help_text = match.group(1), match.group(2)\r
+                    alias = self._aliases.get(name) or ''\r
+                    if alias:\r
+                        alias = "(%s)" % alias\r
+                    line = "  %-22s%s" % ("%s %s" % (name, alias), help_text)\r
+            if line.strip().startswith('subcommands:'):\r
+                line = 'Subcommands:'\r
+                subcomm = True\r
+            result.append(line)\r
+        return '\n'.join(result)\r
+\r
+def subparser(func):\r
+    """Convenient decorator for subparsers."""\r
+    @functools.wraps(func)\r
+    def wrapper(parser):\r
+        """\r
+        Create subparser\r
+        Set first line of function's docstring as a help\r
+        and the rest of the lines as a description.\r
+        Set attribute 'module' of subparser to 'cmd'+first part of function name\r
+        """\r
+        splitted = func.__doc__.split('\n')\r
+        name = func.__name__.split('_')[0]\r
+        subpar = parser.add_parser(name, help=splitted[0],\r
+                                   description='\n'.join(splitted[1:]),\r
+                                   formatter_class=RawDescriptionHelpFormatter)\r
+        subpar.set_defaults(module="cmd_%s" % name)\r
+        return func(subpar)\r
+    return wrapper\r
+\r
diff --git a/mic/utils/cmdln.py b/mic/utils/cmdln.py
deleted file mode 100644 (file)
index 3f4bb0f..0000000
+++ /dev/null
@@ -1,1586 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2002-2007 ActiveState Software Inc.
-# License: MIT (see LICENSE.txt for license details)
-# Author:  Trent Mick
-# Home:    http://trentm.com/projects/cmdln/
-
-"""An improvement on Python's standard cmd.py module.
-
-As with cmd.py, this module provides "a simple framework for writing
-line-oriented command intepreters."  This module provides a 'RawCmdln'
-class that fixes some design flaws in cmd.Cmd, making it more scalable
-and nicer to use for good 'cvs'- or 'svn'-style command line interfaces
-or simple shells.  And it provides a 'Cmdln' class that add
-optparse-based option processing. Basically you use it like this:
-
-    import cmdln
-
-    class MySVN(cmdln.Cmdln):
-        name = "svn"
-
-        @cmdln.alias('stat', 'st')
-        @cmdln.option('-v', '--verbose', action='store_true'
-                      help='print verbose information')
-        def do_status(self, subcmd, opts, *paths):
-            print "handle 'svn status' command"
-
-        #...
-
-    if __name__ == "__main__":
-        shell = MySVN()
-        retval = shell.main()
-        sys.exit(retval)
-
-See the README.txt or <http://trentm.com/projects/cmdln/> for more
-details.
-"""
-
-__version_info__ = (1, 1, 2)
-__version__ = '.'.join(map(str, __version_info__))
-
-import os
-import sys
-import re
-import cmd
-import optparse
-from pprint import pprint
-import sys
-
-
-
-
-#---- globals
-
-LOOP_ALWAYS, LOOP_NEVER, LOOP_IF_EMPTY = range(3)
-
-# An unspecified optional argument when None is a meaningful value.
-_NOT_SPECIFIED = ("Not", "Specified")
-
-# Pattern to match a TypeError message from a call that
-# failed because of incorrect number of arguments (see
-# Python/getargs.c).
-_INCORRECT_NUM_ARGS_RE = re.compile(
-    r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))")
-
-
-
-#---- exceptions
-
-class CmdlnError(Exception):
-    """A cmdln.py usage error."""
-    def __init__(self, msg):
-        self.msg = msg
-    def __str__(self):
-        return self.msg
-
-class CmdlnUserError(Exception):
-    """An error by a user of a cmdln-based tool/shell."""
-    pass
-
-
-
-#---- public methods and classes
-
-def alias(*aliases):
-    """Decorator to add aliases for Cmdln.do_* command handlers.
-    
-    Example:
-        class MyShell(cmdln.Cmdln):
-            @cmdln.alias("!", "sh")
-            def do_shell(self, argv):
-                #...implement 'shell' command
-    """
-    def decorate(f):
-        if not hasattr(f, "aliases"):
-            f.aliases = []
-        f.aliases += aliases
-        return f
-    return decorate
-
-
-class RawCmdln(cmd.Cmd):
-    """An improved (on cmd.Cmd) framework for building multi-subcommand
-    scripts (think "svn" & "cvs") and simple shells (think "pdb" and
-    "gdb").
-
-    A simple example:
-
-        import cmdln
-
-        class MySVN(cmdln.RawCmdln):
-            name = "svn"
-
-            @cmdln.aliases('stat', 'st')
-            def do_status(self, argv):
-                print "handle 'svn status' command"
-
-        if __name__ == "__main__":
-            shell = MySVN()
-            retval = shell.main()
-            sys.exit(retval)
-
-    See <http://trentm.com/projects/cmdln> for more information.
-    """
-    name = None      # if unset, defaults basename(sys.argv[0])
-    prompt = None    # if unset, defaults to self.name+"> "
-    version = None   # if set, default top-level options include --version
-
-    # Default messages for some 'help' command error cases.
-    # They are interpolated with one arg: the command.
-    nohelp = "no help on '%s'"
-    unknowncmd = "unknown command: '%s'"
-
-    helpindent = '' # string with which to indent help output
-
-    def __init__(self, completekey='tab', 
-                 stdin=None, stdout=None, stderr=None):
-        """Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None)
-
-        The optional argument 'completekey' is the readline name of a
-        completion key; it defaults to the Tab key. If completekey is
-        not None and the readline module is available, command completion
-        is done automatically.
-        
-        The optional arguments 'stdin', 'stdout' and 'stderr' specify
-        alternate input, output and error output file objects; if not
-        specified, sys.* are used.
-        
-        If 'stdout' but not 'stderr' is specified, stdout is used for
-        error output. This is to provide least surprise for users used
-        to only the 'stdin' and 'stdout' options with cmd.Cmd.
-        """
-        import sys
-        if self.name is None:
-            self.name = os.path.basename(sys.argv[0])
-        if self.prompt is None:
-            self.prompt = self.name+"> "
-        self._name_str = self._str(self.name)
-        self._prompt_str = self._str(self.prompt)
-        if stdin is not None:
-            self.stdin = stdin
-        else:
-            self.stdin = sys.stdin
-        if stdout is not None:
-            self.stdout = stdout
-        else:
-            self.stdout = sys.stdout
-        if stderr is not None:
-            self.stderr = stderr
-        elif stdout is not None:
-            self.stderr = stdout
-        else:
-            self.stderr = sys.stderr
-        self.cmdqueue = []
-        self.completekey = completekey
-        self.cmdlooping = False
-
-    def get_optparser(self):
-        """Hook for subclasses to set the option parser for the
-        top-level command/shell.
-
-        This option parser is used retrieved and used by `.main()' to
-        handle top-level options.
-
-        The default implements a single '-h|--help' option. Sub-classes
-        can return None to have no options at the top-level. Typically
-        an instance of CmdlnOptionParser should be returned.
-        """
-        version = (self.version is not None 
-                    and "%s %s" % (self._name_str, self.version)
-                    or None)
-        return CmdlnOptionParser(self, version=version)
-
-    def postoptparse(self):
-        """Hook method executed just after `.main()' parses top-level
-        options.
-
-        When called `self.options' holds the results of the option parse.
-        """
-        pass
-
-    def main(self, argv=None, loop=LOOP_NEVER):
-        """A possible mainline handler for a script, like so:
-
-            import cmdln
-            class MyCmd(cmdln.Cmdln):
-                name = "mycmd"
-                ...
-            
-            if __name__ == "__main__":
-                MyCmd().main()
-
-        By default this will use sys.argv to issue a single command to
-        'MyCmd', then exit. The 'loop' argument can be use to control
-        interactive shell behaviour.
-        
-        Arguments:
-            "argv" (optional, default sys.argv) is the command to run.
-                It must be a sequence, where the first element is the
-                command name and subsequent elements the args for that
-                command.
-            "loop" (optional, default LOOP_NEVER) is a constant
-                indicating if a command loop should be started (i.e. an
-                interactive shell). Valid values (constants on this module):
-                    LOOP_ALWAYS     start loop and run "argv", if any
-                    LOOP_NEVER      run "argv" (or .emptyline()) and exit
-                    LOOP_IF_EMPTY   run "argv", if given, and exit;
-                                    otherwise, start loop
-        """
-        if argv is None:
-            import sys
-            argv = sys.argv
-        else:
-            argv = argv[:] # don't modify caller's list
-
-        self.optparser = self.get_optparser()
-        if self.optparser: # i.e. optparser=None means don't process for opts
-            try:
-                self.options, args = self.optparser.parse_args(argv[1:])
-            except CmdlnUserError, ex:
-                msg = "%s: %s\nTry '%s help' for info.\n"\
-                      % (self.name, ex, self.name)
-                self.stderr.write(self._str(msg))
-                self.stderr.flush()
-                return 1
-            except StopOptionProcessing, ex:
-                return 0
-        else:
-            self.options, args = None, argv[1:]
-        self.postoptparse()
-
-        if loop == LOOP_ALWAYS:
-            if args:
-                self.cmdqueue.append(args)
-            return self.cmdloop()
-        elif loop == LOOP_NEVER:
-            if args:
-                return self.cmd(args)
-            else:
-                return self.emptyline()
-        elif loop == LOOP_IF_EMPTY:
-            if args:
-                return self.cmd(args)
-            else:
-                return self.cmdloop()
-
-    def cmd(self, argv):
-        """Run one command and exit.
-        
-            "argv" is the arglist for the command to run. argv[0] is the
-                command to run. If argv is an empty list then the
-                'emptyline' handler is run.
-
-        Returns the return value from the command handler.
-        """
-        assert isinstance(argv, (list, tuple)), \
-                "'argv' is not a sequence: %r" % argv
-        retval = None
-        try:
-            argv = self.precmd(argv)
-            retval = self.onecmd(argv)
-            self.postcmd(argv)
-        except:
-            if not self.cmdexc(argv):
-                raise
-            retval = 1
-        return retval
-
-    def _str(self, s):
-        """Safely convert the given str/unicode to a string for printing."""
-        try:
-            return str(s)
-        except UnicodeError:
-            #XXX What is the proper encoding to use here? 'utf-8' seems
-            #    to work better than "getdefaultencoding" (usually
-            #    'ascii'), on OS X at least.
-            #import sys
-            #return s.encode(sys.getdefaultencoding(), "replace")
-            return s.encode("utf-8", "replace")
-
-    def cmdloop(self, intro=None):
-        """Repeatedly issue a prompt, accept input, parse into an argv, and
-        dispatch (via .precmd(), .onecmd() and .postcmd()), passing them
-        the argv. In other words, start a shell.
-        
-            "intro" (optional) is a introductory message to print when
-                starting the command loop. This overrides the class
-                "intro" attribute, if any.
-        """
-        self.cmdlooping = True
-        self.preloop()
-        if self.use_rawinput and self.completekey:
-            try:
-                import readline
-                self.old_completer = readline.get_completer()
-                readline.set_completer(self.complete)
-                readline.parse_and_bind(self.completekey+": complete")
-            except ImportError:
-                pass
-        try:
-            if intro is None:
-                intro = self.intro
-            if intro:
-                intro_str = self._str(intro)
-                self.stdout.write(intro_str+'\n')
-            self.stop = False
-            retval = None
-            while not self.stop:
-                if self.cmdqueue:
-                    argv = self.cmdqueue.pop(0)
-                    assert isinstance(argv, (list, tuple)), \
-                            "item on 'cmdqueue' is not a sequence: %r" % argv
-                else:
-                    if self.use_rawinput:
-                        try:
-                            line = raw_input(self._prompt_str)
-                        except EOFError:
-                            line = 'EOF'
-                    else:
-                        self.stdout.write(self._prompt_str)
-                        self.stdout.flush()
-                        line = self.stdin.readline()
-                        if not len(line):
-                            line = 'EOF'
-                        else:
-                            line = line[:-1] # chop '\n'
-                    argv = line2argv(line)
-                try:
-                    argv = self.precmd(argv)
-                    retval = self.onecmd(argv)
-                    self.postcmd(argv)
-                except:
-                    if not self.cmdexc(argv):
-                        raise
-                    retval = 1
-                self.lastretval = retval
-            self.postloop()
-        finally:
-            if self.use_rawinput and self.completekey:
-                try:
-                    import readline
-                    readline.set_completer(self.old_completer)
-                except ImportError:
-                    pass
-        self.cmdlooping = False
-        return retval
-
-    def precmd(self, argv):
-        """Hook method executed just before the command argv is
-        interpreted, but after the input prompt is generated and issued.
-
-            "argv" is the cmd to run.
-            
-        Returns an argv to run (i.e. this method can modify the command
-        to run).
-        """
-        return argv
-
-    def postcmd(self, argv):
-        """Hook method executed just after a command dispatch is finished.
-        
-            "argv" is the command that was run.
-        """
-        pass
-
-    def cmdexc(self, argv):
-        """Called if an exception is raised in any of precmd(), onecmd(),
-        or postcmd(). If True is returned, the exception is deemed to have
-        been dealt with. Otherwise, the exception is re-raised.
-
-        The default implementation handles CmdlnUserError's, which
-        typically correspond to user error in calling commands (as
-        opposed to programmer error in the design of the script using
-        cmdln.py).
-        """
-        import sys
-        type, exc, traceback = sys.exc_info()
-        if isinstance(exc, CmdlnUserError):
-            msg = "%s %s: %s\nTry '%s help %s' for info.\n"\
-                  % (self.name, argv[0], exc, self.name, argv[0])
-            self.stderr.write(self._str(msg))
-            self.stderr.flush()
-            return True
-
-    def onecmd(self, argv):
-        if not argv:
-            return self.emptyline()
-        self.lastcmd = argv
-        cmdname = self._get_canonical_cmd_name(argv[0])
-        if cmdname:
-            handler = self._get_cmd_handler(cmdname)
-            if handler:
-                return self._dispatch_cmd(handler, argv)
-        return self.default(argv)
-
-    def _dispatch_cmd(self, handler, argv):
-        return handler(argv)
-
-    def default(self, argv):
-        """Hook called to handle a command for which there is no handler.
-
-            "argv" is the command and arguments to run.
-        
-        The default implementation writes and error message to stderr
-        and returns an error exit status.
-
-        Returns a numeric command exit status.
-        """
-        errmsg = self._str(self.unknowncmd % (argv[0],))
-        if self.cmdlooping:
-            self.stderr.write(errmsg+"\n")
-        else:
-            self.stderr.write("%s: %s\nTry '%s help' for info.\n"
-                              % (self._name_str, errmsg, self._name_str))
-        self.stderr.flush()
-        return 1
-
-    def parseline(self, line):
-        # This is used by Cmd.complete (readline completer function) to
-        # massage the current line buffer before completion processing.
-        # We override to drop special '!' handling.
-        line = line.strip()
-        if not line:
-            return None, None, line
-        elif line[0] == '?':
-            line = 'help ' + line[1:]
-        i, n = 0, len(line)
-        while i < n and line[i] in self.identchars: i = i+1
-        cmd, arg = line[:i], line[i:].strip()
-        return cmd, arg, line
-
-    def helpdefault(self, cmd, known):
-        """Hook called to handle help on a command for which there is no
-        help handler.
-
-            "cmd" is the command name on which help was requested.
-            "known" is a boolean indicating if this command is known
-                (i.e. if there is a handler for it).
-        
-        Returns a return code.
-        """
-        if known:
-            msg = self._str(self.nohelp % (cmd,))
-            if self.cmdlooping:
-                self.stderr.write(msg + '\n')
-            else:
-                self.stderr.write("%s: %s\n" % (self.name, msg))
-        else:
-            msg = self.unknowncmd % (cmd,)
-            if self.cmdlooping:
-                self.stderr.write(msg + '\n')
-            else:
-                self.stderr.write("%s: %s\n"
-                                  "Try '%s help' for info.\n"
-                                  % (self.name, msg, self.name))
-        self.stderr.flush()
-        return 1
-
-    def do_help(self, argv):
-        """${cmd_name}: give detailed help on a specific sub-command
-
-        Usage:
-            ${name} help [COMMAND]
-        """
-        if len(argv) > 1: # asking for help on a particular command
-            doc = None
-            cmdname = self._get_canonical_cmd_name(argv[1]) or argv[1]
-            if not cmdname:
-                return self.helpdefault(argv[1], False)
-            else:
-                helpfunc = getattr(self, "help_"+cmdname, None)
-                if helpfunc:
-                    doc = helpfunc()
-                else:
-                    handler = self._get_cmd_handler(cmdname)
-                    if handler:
-                        doc = handler.__doc__
-                    if doc is None:
-                        return self.helpdefault(argv[1], handler != None)
-        else: # bare "help" command
-            doc = self.__class__.__doc__  # try class docstring
-            if doc is None:
-                # Try to provide some reasonable useful default help.
-                if self.cmdlooping: prefix = ""
-                else:               prefix = self.name+' '
-                doc = """Usage:
-                    %sCOMMAND [ARGS...]
-                    %shelp [COMMAND]
-
-                ${option_list}
-                ${command_list}
-                ${help_list}
-                """ % (prefix, prefix)
-            cmdname = None
-
-        if doc: # *do* have help content, massage and print that
-            doc = self.help_reindent(doc)
-            doc = self.help_preprocess(doc, cmdname)
-            doc = doc.rstrip() + '\n' # trim down trailing space
-            self.stdout.write(self._str(doc))
-            self.stdout.flush()
-    do_help.aliases = ["?"]
-
-    def help_reindent(self, help, indent=None):
-        """Hook to re-indent help strings before writing to stdout.
-
-            "help" is the help content to re-indent
-            "indent" is a string with which to indent each line of the
-                help content after normalizing. If unspecified or None
-                then the default is use: the 'self.helpindent' class
-                attribute. By default this is the empty string, i.e.
-                no indentation.
-
-        By default, all common leading whitespace is removed and then
-        the lot is indented by 'self.helpindent'. When calculating the
-        common leading whitespace the first line is ignored -- hence
-        help content for Conan can be written as follows and have the
-        expected indentation:
-
-            def do_crush(self, ...):
-                '''${cmd_name}: crush your enemies, see them driven before you...
-
-                c.f. Conan the Barbarian'''
-        """
-        if indent is None:
-            indent = self.helpindent
-        lines = help.splitlines(0)
-        _dedentlines(lines, skip_first_line=True)
-        lines = [(indent+line).rstrip() for line in lines]
-        return '\n'.join(lines)
-
-    def help_preprocess(self, help, cmdname):
-        """Hook to preprocess a help string before writing to stdout.
-
-            "help" is the help string to process.
-            "cmdname" is the canonical sub-command name for which help
-                is being given, or None if the help is not specific to a
-                command.
-
-        By default the following template variables are interpolated in
-        help content. (Note: these are similar to Python 2.4's
-        string.Template interpolation but not quite.)
-
-        ${name}
-            The tool's/shell's name, i.e. 'self.name'.
-        ${option_list}
-            A formatted table of options for this shell/tool.
-        ${command_list}
-            A formatted table of available sub-commands.
-        ${help_list}
-            A formatted table of additional help topics (i.e. 'help_*'
-            methods with no matching 'do_*' method).
-        ${cmd_name}
-            The name (and aliases) for this sub-command formatted as:
-            "NAME (ALIAS1, ALIAS2, ...)".
-        ${cmd_usage}
-            A formatted usage block inferred from the command function
-            signature.
-        ${cmd_option_list}
-            A formatted table of options for this sub-command. (This is
-            only available for commands using the optparse integration,
-            i.e.  using @cmdln.option decorators or manually setting the
-            'optparser' attribute on the 'do_*' method.)
-
-        Returns the processed help. 
-        """
-        preprocessors = {
-            "${name}":            self._help_preprocess_name,
-            "${option_list}":     self._help_preprocess_option_list,
-            "${command_list}":    self._help_preprocess_command_list,
-            "${help_list}":       self._help_preprocess_help_list,
-            "${cmd_name}":        self._help_preprocess_cmd_name,
-            "${cmd_usage}":       self._help_preprocess_cmd_usage,
-            "${cmd_option_list}": self._help_preprocess_cmd_option_list,
-        }
-
-        for marker, preprocessor in preprocessors.items():
-            if marker in help:
-                help = preprocessor(help, cmdname)
-        return help
-
-    def _help_preprocess_name(self, help, cmdname=None):
-        return help.replace("${name}", self.name)
-
-    def _help_preprocess_option_list(self, help, cmdname=None):
-        marker = "${option_list}"
-        indent, indent_width = _get_indent(marker, help)
-        suffix = _get_trailing_whitespace(marker, help)
-
-        if self.optparser:
-            # Setup formatting options and format.
-            # - Indentation of 4 is better than optparse default of 2.
-            #   C.f. Damian Conway's discussion of this in Perl Best
-            #   Practices.
-            self.optparser.formatter.indent_increment = 4
-            self.optparser.formatter.current_indent = indent_width
-            block = self.optparser.format_option_help() + '\n'
-        else:
-            block = ""
-            
-        help = help.replace(indent+marker+suffix, block, 1)
-        return help
-
-
-    def _help_preprocess_command_list(self, help, cmdname=None):
-        marker = "${command_list}"
-        indent, indent_width = _get_indent(marker, help)
-        suffix = _get_trailing_whitespace(marker, help)
-
-        # Find any aliases for commands.
-        token2canonical = self._get_canonical_map()
-        aliases = {}
-        for token, cmdname in token2canonical.items():
-            if token == cmdname: continue
-            aliases.setdefault(cmdname, []).append(token)
-
-        # Get the list of (non-hidden) commands and their
-        # documentation, if any.
-        cmdnames = {} # use a dict to strip duplicates
-        for attr in self.get_names():
-            if attr.startswith("do_"):
-                cmdnames[attr[3:]] = True
-        cmdnames = cmdnames.keys()
-        cmdnames.sort()
-        linedata = []
-        for cmdname in cmdnames:
-            if aliases.get(cmdname):
-                a = aliases[cmdname]
-                a.sort()
-                cmdstr = "%s (%s)" % (cmdname, ", ".join(a))
-            else:
-                cmdstr = cmdname
-            doc = None
-            try:
-                helpfunc = getattr(self, 'help_'+cmdname)
-            except AttributeError:
-                handler = self._get_cmd_handler(cmdname)
-                if handler:
-                    doc = handler.__doc__
-            else:
-                doc = helpfunc()
-                
-            # Strip "${cmd_name}: " from the start of a command's doc. Best
-            # practice dictates that command help strings begin with this, but
-            # it isn't at all wanted for the command list.
-            to_strip = "${cmd_name}:"
-            if doc and doc.startswith(to_strip):
-                #log.debug("stripping %r from start of %s's help string",
-                #          to_strip, cmdname)
-                doc = doc[len(to_strip):].lstrip()
-            linedata.append( (cmdstr, doc) )
-
-        if linedata:
-            subindent = indent + ' '*4
-            lines = _format_linedata(linedata, subindent, indent_width+4)
-            block = indent + "Commands:\n" \
-                    + '\n'.join(lines) + "\n\n"
-            help = help.replace(indent+marker+suffix, block, 1)
-        return help
-
-    def _gen_names_and_attrs(self):
-        # Inheritance says we have to look in class and
-        # base classes; order is not important.
-        names = []
-        classes = [self.__class__]
-        while classes:
-            aclass = classes.pop(0)
-            if aclass.__bases__:
-                classes = classes + list(aclass.__bases__)
-            for name in dir(aclass):
-                yield (name, getattr(aclass, name))
-
-    def _help_preprocess_help_list(self, help, cmdname=None):
-        marker = "${help_list}"
-        indent, indent_width = _get_indent(marker, help)
-        suffix = _get_trailing_whitespace(marker, help)
-
-        # Determine the additional help topics, if any.
-        helpnames = {}
-        token2cmdname = self._get_canonical_map()
-        for attrname, attr in self._gen_names_and_attrs():
-            if not attrname.startswith("help_"): continue
-            helpname = attrname[5:]
-            if helpname not in token2cmdname:
-                helpnames[helpname] = attr
-
-        if helpnames:
-            linedata = [(n, a.__doc__ or "") for n, a in helpnames.items()]
-            linedata.sort()
-
-            subindent = indent + ' '*4
-            lines = _format_linedata(linedata, subindent, indent_width+4)
-            block = (indent
-                    + "Additional help topics (run `%s help TOPIC'):\n" % self.name
-                    + '\n'.join(lines)
-                    + "\n\n")
-        else:
-            block = ''
-        help = help.replace(indent+marker+suffix, block, 1)
-        return help
-
-    def _help_preprocess_cmd_name(self, help, cmdname=None):
-        marker = "${cmd_name}"
-        handler = self._get_cmd_handler(cmdname)
-        if not handler:
-            raise CmdlnError("cannot preprocess '%s' into help string: "
-                             "could not find command handler for %r" 
-                             % (marker, cmdname))
-        s = cmdname
-        if hasattr(handler, "aliases"):
-            s += " (%s)" % (", ".join(handler.aliases))
-        help = help.replace(marker, s)
-        return help
-
-    #TODO: this only makes sense as part of the Cmdln class.
-    #      Add hooks to add help preprocessing template vars and put
-    #      this one on that class.
-    def _help_preprocess_cmd_usage(self, help, cmdname=None):
-        marker = "${cmd_usage}"
-        handler = self._get_cmd_handler(cmdname)
-        if not handler:
-            raise CmdlnError("cannot preprocess '%s' into help string: "
-                             "could not find command handler for %r" 
-                             % (marker, cmdname))
-        indent, indent_width = _get_indent(marker, help)
-        suffix = _get_trailing_whitespace(marker, help)
-
-        # Extract the introspection bits we need.
-        func = handler.im_func
-        if func.func_defaults:
-            func_defaults = list(func.func_defaults)
-        else:
-            func_defaults = []
-        co_argcount = func.func_code.co_argcount
-        co_varnames = func.func_code.co_varnames
-        co_flags = func.func_code.co_flags
-        CO_FLAGS_ARGS = 4
-        CO_FLAGS_KWARGS = 8
-
-        # Adjust argcount for possible *args and **kwargs arguments.
-        argcount = co_argcount
-        if co_flags & CO_FLAGS_ARGS:   argcount += 1
-        if co_flags & CO_FLAGS_KWARGS: argcount += 1
-
-        # Determine the usage string.
-        usage = "%s %s" % (self.name, cmdname)
-        if argcount <= 2:   # handler ::= do_FOO(self, argv)
-            usage += " [ARGS...]"
-        elif argcount >= 3: # handler ::= do_FOO(self, subcmd, opts, ...)
-            argnames = list(co_varnames[3:argcount])
-            tail = ""
-            if co_flags & CO_FLAGS_KWARGS:
-                name = argnames.pop(-1)
-                import warnings
-                # There is no generally accepted mechanism for passing
-                # keyword arguments from the command line. Could
-                # *perhaps* consider: arg=value arg2=value2 ...
-                warnings.warn("argument '**%s' on '%s.%s' command "
-                              "handler will never get values" 
-                              % (name, self.__class__.__name__,
-                                 func.func_name))
-            if co_flags & CO_FLAGS_ARGS:
-                name = argnames.pop(-1)
-                tail = "[%s...]" % name.upper()
-            while func_defaults:
-                func_defaults.pop(-1)
-                name = argnames.pop(-1)
-                tail = "[%s%s%s]" % (name.upper(), (tail and ' ' or ''), tail)
-            while argnames:
-                name = argnames.pop(-1)
-                tail = "%s %s" % (name.upper(), tail)
-            usage += ' ' + tail
-
-        block_lines = [
-            self.helpindent + "Usage:",
-            self.helpindent + ' '*4 + usage
-        ]
-        block = '\n'.join(block_lines) + '\n\n'
-
-        help = help.replace(indent+marker+suffix, block, 1)
-        return help
-
-    #TODO: this only makes sense as part of the Cmdln class.
-    #      Add hooks to add help preprocessing template vars and put
-    #      this one on that class.
-    def _help_preprocess_cmd_option_list(self, help, cmdname=None):
-        marker = "${cmd_option_list}"
-        handler = self._get_cmd_handler(cmdname)
-        if not handler:
-            raise CmdlnError("cannot preprocess '%s' into help string: "
-                             "could not find command handler for %r" 
-                             % (marker, cmdname))
-        indent, indent_width = _get_indent(marker, help)
-        suffix = _get_trailing_whitespace(marker, help)
-        if hasattr(handler, "optparser"):
-            # Setup formatting options and format.
-            # - Indentation of 4 is better than optparse default of 2.
-            #   C.f. Damian Conway's discussion of this in Perl Best
-            #   Practices.
-            handler.optparser.formatter.indent_increment = 4
-            handler.optparser.formatter.current_indent = indent_width
-            block = handler.optparser.format_option_help() + '\n'
-        else:
-            block = ""
-
-        help = help.replace(indent+marker+suffix, block, 1)
-        return help
-
-    def _get_canonical_cmd_name(self, token):
-        map = self._get_canonical_map()
-        return map.get(token, None)
-
-    def _get_canonical_map(self):
-        """Return a mapping of available command names and aliases to
-        their canonical command name.
-        """
-        cacheattr = "_token2canonical"
-        if not hasattr(self, cacheattr):
-            # Get the list of commands and their aliases, if any.
-            token2canonical = {}
-            cmd2funcname = {} # use a dict to strip duplicates
-            for attr in self.get_names():
-                if attr.startswith("do_"):    cmdname = attr[3:]
-                elif attr.startswith("_do_"): cmdname = attr[4:]
-                else:
-                    continue
-                cmd2funcname[cmdname] = attr
-                token2canonical[cmdname] = cmdname
-            for cmdname, funcname in cmd2funcname.items(): # add aliases
-                func = getattr(self, funcname)
-                aliases = getattr(func, "aliases", [])
-                for alias in aliases:
-                    if alias in cmd2funcname:
-                        import warnings
-                        warnings.warn("'%s' alias for '%s' command conflicts "
-                                      "with '%s' handler"
-                                      % (alias, cmdname, cmd2funcname[alias]))
-                        continue
-                    token2canonical[alias] = cmdname
-            setattr(self, cacheattr, token2canonical)
-        return getattr(self, cacheattr)
-
-    def _get_cmd_handler(self, cmdname):
-        handler = None
-        try:
-            handler = getattr(self, 'do_' + cmdname)
-        except AttributeError:
-            try:
-                # Private command handlers begin with "_do_".
-                handler = getattr(self, '_do_' + cmdname)
-            except AttributeError:
-                pass
-        return handler
-
-    def _do_EOF(self, argv):
-        # Default EOF handler
-        # Note: an actual EOF is redirected to this command.
-        #TODO: separate name for this. Currently it is available from
-        #      command-line. Is that okay?
-        self.stdout.write('\n')
-        self.stdout.flush()
-        self.stop = True
-
-    def emptyline(self):
-        # Different from cmd.Cmd: don't repeat the last command for an
-        # emptyline.
-        if self.cmdlooping:
-            pass
-        else:
-            return self.do_help(["help"])
-
-
-#---- optparse.py extension to fix (IMO) some deficiencies
-#
-# See the class _OptionParserEx docstring for details.
-#
-
-class StopOptionProcessing(Exception):
-    """Indicate that option *and argument* processing should stop
-    cleanly. This is not an error condition. It is similar in spirit to
-    StopIteration. This is raised by _OptionParserEx's default "help"
-    and "version" option actions and can be raised by custom option
-    callbacks too.
-    
-    Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx)
-    usage is:
-
-        parser = CmdlnOptionParser(mycmd)
-        parser.add_option("-f", "--force", dest="force")
-        ...
-        try:
-            opts, args = parser.parse_args()
-        except StopOptionProcessing:
-            # normal termination, "--help" was probably given
-            sys.exit(0)
-    """
-
-class _OptionParserEx(optparse.OptionParser):
-    """An optparse.OptionParser that uses exceptions instead of sys.exit.
-
-    This class is an extension of optparse.OptionParser that differs
-    as follows:
-    - Correct (IMO) the default OptionParser error handling to never
-      sys.exit(). Instead OptParseError exceptions are passed through.
-    - Add the StopOptionProcessing exception (a la StopIteration) to
-      indicate normal termination of option processing.
-      See StopOptionProcessing's docstring for details.
-
-    I'd also like to see the following in the core optparse.py, perhaps
-    as a RawOptionParser which would serve as a base class for the more
-    generally used OptionParser (that works as current):
-    - Remove the implicit addition of the -h|--help and --version
-      options. They can get in the way (e.g. if want '-?' and '-V' for
-      these as well) and it is not hard to do:
-        optparser.add_option("-h", "--help", action="help")
-        optparser.add_option("--version", action="version")
-      These are good practices, just not valid defaults if they can
-      get in the way.
-    """
-    def error(self, msg):
-        raise optparse.OptParseError(msg)
-
-    def exit(self, status=0, msg=None):
-        if status == 0:
-            raise StopOptionProcessing(msg)
-        else:
-            #TODO: don't lose status info here
-            raise optparse.OptParseError(msg)
-
-
-
-#---- optparse.py-based option processing support
-
-class CmdlnOptionParser(_OptionParserEx):
-    """An optparse.OptionParser class more appropriate for top-level
-    Cmdln options. For parsing of sub-command options, see
-    SubCmdOptionParser.
-
-    Changes:
-    - disable_interspersed_args() by default, because a Cmdln instance
-      has sub-commands which may themselves have options.
-    - Redirect print_help() to the Cmdln.do_help() which is better
-      equiped to handle the "help" action.
-    - error() will raise a CmdlnUserError: OptionParse.error() is meant
-      to be called for user errors. Raising a well-known error here can
-      make error handling clearer.
-    - Also see the changes in _OptionParserEx.
-    """
-    def __init__(self, cmdln, **kwargs):
-        self.cmdln = cmdln
-        kwargs["prog"] = self.cmdln.name
-        _OptionParserEx.__init__(self, **kwargs)
-        self.disable_interspersed_args()
-
-    def print_help(self, file=None):
-        self.cmdln.onecmd(["help"])
-
-    def error(self, msg):
-        raise CmdlnUserError(msg)
-
-
-class SubCmdOptionParser(_OptionParserEx):
-    def set_cmdln_info(self, cmdln, subcmd):
-        """Called by Cmdln to pass relevant info about itself needed
-        for print_help().
-        """
-        self.cmdln = cmdln
-        self.subcmd = subcmd
-
-    def print_help(self, file=None):
-        self.cmdln.onecmd(["help", self.subcmd])
-
-    def error(self, msg):
-        raise CmdlnUserError(msg)
-
-
-def option(*args, **kwargs):
-    """Decorator to add an option to the optparser argument of a Cmdln
-    subcommand.
-    
-    Example:
-        class MyShell(cmdln.Cmdln):
-            @cmdln.option("-f", "--force", help="force removal")
-            def do_remove(self, subcmd, opts, *args):
-                #...
-    """
-    #XXX Is there a possible optimization for many options to not have a
-    #    large stack depth here?
-    def decorate(f):
-        if not hasattr(f, "optparser"):
-            f.optparser = SubCmdOptionParser()
-        f.optparser.add_option(*args, **kwargs)
-        return f
-    return decorate
-
-
-class Cmdln(RawCmdln):
-    """An improved (on cmd.Cmd) framework for building multi-subcommand
-    scripts (think "svn" & "cvs") and simple shells (think "pdb" and
-    "gdb").
-
-    A simple example:
-
-        import cmdln
-
-        class MySVN(cmdln.Cmdln):
-            name = "svn"
-
-            @cmdln.aliases('stat', 'st')
-            @cmdln.option('-v', '--verbose', action='store_true'
-                          help='print verbose information')
-            def do_status(self, subcmd, opts, *paths):
-                print "handle 'svn status' command"
-
-            #...
-
-        if __name__ == "__main__":
-            shell = MySVN()
-            retval = shell.main()
-            sys.exit(retval)
-
-    'Cmdln' extends 'RawCmdln' by providing optparse option processing
-    integration.  See this class' _dispatch_cmd() docstring and
-    <http://trentm.com/projects/cmdln> for more information.
-    """
-    def _dispatch_cmd(self, handler, argv):
-        """Introspect sub-command handler signature to determine how to
-        dispatch the command. The raw handler provided by the base
-        'RawCmdln' class is still supported:
-
-            def do_foo(self, argv):
-                # 'argv' is the vector of command line args, argv[0] is
-                # the command name itself (i.e. "foo" or an alias)
-                pass
-
-        In addition, if the handler has more than 2 arguments option
-        processing is automatically done (using optparse):
-
-            @cmdln.option('-v', '--verbose', action='store_true')
-            def do_bar(self, subcmd, opts, *args):
-                # subcmd = <"bar" or an alias>
-                # opts = <an optparse.Values instance>
-                if opts.verbose:
-                    print "lots of debugging output..."
-                # args = <tuple of arguments>
-                for arg in args:
-                    bar(arg)
-
-        TODO: explain that "*args" can be other signatures as well.
-
-        The `cmdln.option` decorator corresponds to an `add_option()`
-        method call on an `optparse.OptionParser` instance.
-
-        You can declare a specific number of arguments:
-
-            @cmdln.option('-v', '--verbose', action='store_true')
-            def do_bar2(self, subcmd, opts, bar_one, bar_two):
-                #...
-
-        and an appropriate error message will be raised/printed if the
-        command is called with a different number of args.
-        """
-        co_argcount = handler.im_func.func_code.co_argcount
-        if co_argcount == 2:   # handler ::= do_foo(self, argv)
-            return handler(argv)
-        elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...)
-            try:
-                optparser = handler.optparser
-            except AttributeError:
-                optparser = handler.im_func.optparser = SubCmdOptionParser()
-            assert isinstance(optparser, SubCmdOptionParser)
-            optparser.set_cmdln_info(self, argv[0])
-            try:
-                opts, args = optparser.parse_args(argv[1:])
-            except StopOptionProcessing:
-                #TODO: this doesn't really fly for a replacement of
-                #      optparse.py behaviour, does it?
-                return 0 # Normal command termination
-
-            try:
-                return handler(argv[0], opts, *args)
-            except TypeError, ex:
-                # Some TypeError's are user errors:
-                #   do_foo() takes at least 4 arguments (3 given)
-                #   do_foo() takes at most 5 arguments (6 given)
-                #   do_foo() takes exactly 5 arguments (6 given)
-                # Raise CmdlnUserError for these with a suitably
-                # massaged error message.
-                import sys
-                tb = sys.exc_info()[2] # the traceback object
-                if tb.tb_next is not None:
-                    # If the traceback is more than one level deep, then the
-                    # TypeError do *not* happen on the "handler(...)" call
-                    # above. In that we don't want to handle it specially
-                    # here: it would falsely mask deeper code errors.
-                    raise
-                msg = ex.args[0]
-                match = _INCORRECT_NUM_ARGS_RE.search(msg)
-                if match:
-                    msg = list(match.groups())
-                    msg[1] = int(msg[1]) - 3
-                    if msg[1] == 1:
-                        msg[2] = msg[2].replace("arguments", "argument")
-                    msg[3] = int(msg[3]) - 3
-                    msg = ''.join(map(str, msg))
-                    raise CmdlnUserError(msg)
-                else:
-                    raise
-        else:
-            raise CmdlnError("incorrect argcount for %s(): takes %d, must "
-                             "take 2 for 'argv' signature or 3+ for 'opts' "
-                             "signature" % (handler.__name__, co_argcount))
-        
-
-
-#---- internal support functions
-
-def _format_linedata(linedata, indent, indent_width):
-    """Format specific linedata into a pleasant layout.
-    
-        "linedata" is a list of 2-tuples of the form:
-            (<item-display-string>, <item-docstring>)
-        "indent" is a string to use for one level of indentation
-        "indent_width" is a number of columns by which the
-            formatted data will be indented when printed.
-
-    The <item-display-string> column is held to 15 columns.
-    """
-    lines = []
-    WIDTH = 78 - indent_width
-    SPACING = 2
-    NAME_WIDTH_LOWER_BOUND = 13
-    NAME_WIDTH_UPPER_BOUND = 16
-    NAME_WIDTH = max([len(s) for s,d in linedata])
-    if NAME_WIDTH < NAME_WIDTH_LOWER_BOUND:
-        NAME_WIDTH = NAME_WIDTH_LOWER_BOUND
-    else:
-        NAME_WIDTH = NAME_WIDTH_UPPER_BOUND
-
-    DOC_WIDTH = WIDTH - NAME_WIDTH - SPACING
-    for namestr, doc in linedata:
-        line = indent + namestr
-        if len(namestr) <= NAME_WIDTH:
-            line += ' ' * (NAME_WIDTH + SPACING - len(namestr))
-        else:
-            lines.append(line)
-            line = indent + ' ' * (NAME_WIDTH + SPACING)
-        line += _summarize_doc(doc, DOC_WIDTH)
-        lines.append(line.rstrip())
-    return lines
-
-def _summarize_doc(doc, length=60):
-    r"""Parse out a short one line summary from the given doclines.
-    
-        "doc" is the doc string to summarize.
-        "length" is the max length for the summary
-
-    >>> _summarize_doc("this function does this")
-    'this function does this'
-    >>> _summarize_doc("this function does this", 10)
-    'this fu...'
-    >>> _summarize_doc("this function does this\nand that")
-    'this function does this and that'
-    >>> _summarize_doc("this function does this\n\nand that")
-    'this function does this'
-    """
-    import re
-    if doc is None:
-        return ""
-    assert length > 3, "length <= 3 is absurdly short for a doc summary"
-    doclines = doc.strip().splitlines(0)
-    if not doclines:
-        return ""
-
-    summlines = []
-    for i, line in enumerate(doclines):
-        stripped = line.strip()
-        if not stripped:
-            break
-        summlines.append(stripped)
-        if len(''.join(summlines)) >= length:
-            break
-
-    summary = ' '.join(summlines)
-    if len(summary) > length:
-        summary = summary[:length-3] + "..." 
-    return summary
-
-
-def line2argv(line):
-    r"""Parse the given line into an argument vector.
-    
-        "line" is the line of input to parse.
-
-    This may get niggly when dealing with quoting and escaping. The
-    current state of this parsing may not be completely thorough/correct
-    in this respect.
-    
-    >>> from cmdln import line2argv
-    >>> line2argv("foo")
-    ['foo']
-    >>> line2argv("foo bar")
-    ['foo', 'bar']
-    >>> line2argv("foo bar ")
-    ['foo', 'bar']
-    >>> line2argv(" foo bar")
-    ['foo', 'bar']
-
-    Quote handling:
-    
-    >>> line2argv("'foo bar'")
-    ['foo bar']
-    >>> line2argv('"foo bar"')
-    ['foo bar']
-    >>> line2argv(r'"foo\"bar"')
-    ['foo"bar']
-    >>> line2argv("'foo bar' spam")
-    ['foo bar', 'spam']
-    >>> line2argv("'foo 'bar spam")
-    ['foo bar', 'spam']
-    
-    >>> line2argv('some\tsimple\ttests')
-    ['some', 'simple', 'tests']
-    >>> line2argv('a "more complex" test')
-    ['a', 'more complex', 'test']
-    >>> line2argv('a more="complex test of " quotes')
-    ['a', 'more=complex test of ', 'quotes']
-    >>> line2argv('a more" complex test of " quotes')
-    ['a', 'more complex test of ', 'quotes']
-    >>> line2argv('an "embedded \\"quote\\""')
-    ['an', 'embedded "quote"']
-
-    # Komodo bug 48027
-    >>> line2argv('foo bar C:\\')
-    ['foo', 'bar', 'C:\\']
-
-    # Komodo change 127581
-    >>> line2argv(r'"\test\slash" "foo bar" "foo\"bar"')
-    ['\\test\\slash', 'foo bar', 'foo"bar']
-
-    # Komodo change 127629
-    >>> if sys.platform == "win32":
-    ...     line2argv(r'\foo\bar') == ['\\foo\\bar']
-    ...     line2argv(r'\\foo\\bar') == ['\\\\foo\\\\bar']
-    ...     line2argv('"foo') == ['foo']
-    ... else:
-    ...     line2argv(r'\foo\bar') == ['foobar']
-    ...     line2argv(r'\\foo\\bar') == ['\\foo\\bar']
-    ...     try:
-    ...         line2argv('"foo')
-    ...     except ValueError, ex:
-    ...         "not terminated" in str(ex)
-    True
-    True
-    True
-    """
-    import string
-    line = line.strip()
-    argv = []
-    state = "default"
-    arg = None  # the current argument being parsed
-    i = -1
-    while 1:
-        i += 1
-        if i >= len(line): break
-        ch = line[i]
-
-        if ch == "\\" and i+1 < len(line):
-            # escaped char always added to arg, regardless of state
-            if arg is None: arg = ""
-            if (sys.platform == "win32"
-                or state in ("double-quoted", "single-quoted")
-               ) and line[i+1] not in tuple('"\''):
-                arg += ch
-            i += 1
-            arg += line[i]
-            continue
-
-        if state == "single-quoted":
-            if ch == "'":
-                state = "default"
-            else:
-                arg += ch
-        elif state == "double-quoted":
-            if ch == '"':
-                state = "default"
-            else:
-                arg += ch
-        elif state == "default":
-            if ch == '"':
-                if arg is None: arg = ""
-                state = "double-quoted"
-            elif ch == "'":
-                if arg is None: arg = ""
-                state = "single-quoted"
-            elif ch in string.whitespace:
-                if arg is not None:
-                    argv.append(arg)
-                arg = None
-            else:
-                if arg is None: arg = ""
-                arg += ch
-    if arg is not None:
-        argv.append(arg)
-    if not sys.platform == "win32" and state != "default":
-        raise ValueError("command line is not terminated: unfinished %s "
-                         "segment" % state)
-    return argv
-
-
-def argv2line(argv):
-    r"""Put together the given argument vector into a command line.
-    
-        "argv" is the argument vector to process.
-    
-    >>> from cmdln import argv2line
-    >>> argv2line(['foo'])
-    'foo'
-    >>> argv2line(['foo', 'bar'])
-    'foo bar'
-    >>> argv2line(['foo', 'bar baz'])
-    'foo "bar baz"'
-    >>> argv2line(['foo"bar'])
-    'foo"bar'
-    >>> print argv2line(['foo" bar'])
-    'foo" bar'
-    >>> print argv2line(["foo' bar"])
-    "foo' bar"
-    >>> argv2line(["foo'bar"])
-    "foo'bar"
-    """
-    escapedArgs = []
-    for arg in argv:
-        if ' ' in arg and '"' not in arg:
-            arg = '"'+arg+'"'
-        elif ' ' in arg and "'" not in arg:
-            arg = "'"+arg+"'"
-        elif ' ' in arg:
-            arg = arg.replace('"', r'\"')
-            arg = '"'+arg+'"'
-        escapedArgs.append(arg)
-    return ' '.join(escapedArgs)
-
-
-# Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook
-def _dedentlines(lines, tabsize=8, skip_first_line=False):
-    """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
-    
-        "lines" is a list of lines to dedent.
-        "tabsize" is the tab width to use for indent width calculations.
-        "skip_first_line" is a boolean indicating if the first line should
-            be skipped for calculating the indent width and for dedenting.
-            This is sometimes useful for docstrings and similar.
-    
-    Same as dedent() except operates on a sequence of lines. Note: the
-    lines list is modified **in-place**.
-    """
-    DEBUG = False
-    if DEBUG: 
-        print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
-              % (tabsize, skip_first_line)
-    indents = []
-    margin = None
-    for i, line in enumerate(lines):
-        if i == 0 and skip_first_line: continue
-        indent = 0
-        for ch in line:
-            if ch == ' ':
-                indent += 1
-            elif ch == '\t':
-                indent += tabsize - (indent % tabsize)
-            elif ch in '\r\n':
-                continue # skip all-whitespace lines
-            else:
-                break
-        else:
-            continue # skip all-whitespace lines
-        if DEBUG: print "dedent: indent=%d: %r" % (indent, line)
-        if margin is None:
-            margin = indent
-        else:
-            margin = min(margin, indent)
-    if DEBUG: print "dedent: margin=%r" % margin
-
-    if margin is not None and margin > 0:
-        for i, line in enumerate(lines):
-            if i == 0 and skip_first_line: continue
-            removed = 0
-            for j, ch in enumerate(line):
-                if ch == ' ':
-                    removed += 1
-                elif ch == '\t':
-                    removed += tabsize - (removed % tabsize)
-                elif ch in '\r\n':
-                    if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line
-                    lines[i] = lines[i][j:]
-                    break
-                else:
-                    raise ValueError("unexpected non-whitespace char %r in "
-                                     "line %r while removing %d-space margin"
-                                     % (ch, line, margin))
-                if DEBUG:
-                    print "dedent: %r: %r -> removed %d/%d"\
-                          % (line, ch, removed, margin)
-                if removed == margin:
-                    lines[i] = lines[i][j+1:]
-                    break
-                elif removed > margin:
-                    lines[i] = ' '*(removed-margin) + lines[i][j+1:]
-                    break
-    return lines
-
-def _dedent(text, tabsize=8, skip_first_line=False):
-    """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text
-
-        "text" is the text to dedent.
-        "tabsize" is the tab width to use for indent width calculations.
-        "skip_first_line" is a boolean indicating if the first line should
-            be skipped for calculating the indent width and for dedenting.
-            This is sometimes useful for docstrings and similar.
-    
-    textwrap.dedent(s), but don't expand tabs to spaces
-    """
-    lines = text.splitlines(1)
-    _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line)
-    return ''.join(lines)
-
-
-def _get_indent(marker, s, tab_width=8):
-    """_get_indent(marker, s, tab_width=8) ->
-        (<indentation-of-'marker'>, <indentation-width>)"""
-    # Figure out how much the marker is indented.
-    INDENT_CHARS = tuple(' \t')
-    start = s.index(marker)
-    i = start
-    while i > 0:
-        if s[i-1] not in INDENT_CHARS:
-            break
-        i -= 1
-    indent = s[i:start]
-    indent_width = 0
-    for ch in indent:
-        if ch == ' ':
-            indent_width += 1
-        elif ch == '\t':
-            indent_width += tab_width - (indent_width % tab_width)
-    return indent, indent_width
-
-def _get_trailing_whitespace(marker, s):
-    """Return the whitespace content trailing the given 'marker' in string 's',
-    up to and including a newline.
-    """
-    suffix = ''
-    start = s.index(marker) + len(marker)
-    i = start
-    while i < len(s):
-        if s[i] in ' \t':
-            suffix += s[i]
-        elif s[i] in '\r\n':
-            suffix += s[i]
-            if s[i] == '\r' and i+1 < len(s) and s[i+1] == '\n':
-                suffix += s[i+1]
-            break
-        else:
-            break
-        i += 1
-    return suffix
-
-
-
-#---- bash completion support
-# Note: This is still experimental. I expect to change this
-# significantly.
-#
-# To get Bash completion for a cmdln.Cmdln class, run the following
-# bash command:
-#   $ complete -C 'python -m cmdln /path/to/script.py CmdlnClass' cmdname
-# For example:
-#   $ complete -C 'python -m cmdln ~/bin/svn.py SVN' svn
-#
-#TODO: Simplify the above so don't have to given path to script (try to
-#      find it on PATH, if possible). Could also make class name
-#      optional if there is only one in the module (common case).
-
-if __name__ == "__main__" and len(sys.argv) == 6:
-    def _log(s):
-        return # no-op, comment out for debugging
-        from os.path import expanduser
-        fout = open(expanduser("~/tmp/bashcpln.log"), 'a')
-        fout.write(str(s) + '\n')
-        fout.close()
-
-    # Recipe: module_from_path (1.0.1+)
-    def _module_from_path(path):
-        import imp, os, sys
-        path = os.path.expanduser(path)
-        dir = os.path.dirname(path) or os.curdir
-        name = os.path.splitext(os.path.basename(path))[0]
-        sys.path.insert(0, dir)
-        try:
-            iinfo = imp.find_module(name, [dir])
-            return imp.load_module(name, *iinfo)
-        finally:
-            sys.path.remove(dir)
-
-    def _get_bash_cplns(script_path, class_name, cmd_name,
-                        token, preceding_token):
-        _log('--')
-        _log('get_cplns(%r, %r, %r, %r, %r)'
-             % (script_path, class_name, cmd_name, token, preceding_token))
-        comp_line = os.environ["COMP_LINE"]
-        comp_point = int(os.environ["COMP_POINT"])
-        _log("COMP_LINE: %r" % comp_line)
-        _log("COMP_POINT: %r" % comp_point)
-
-        try:
-            script = _module_from_path(script_path)
-        except ImportError, ex:
-            _log("error importing `%s': %s" % (script_path, ex))
-            return []
-        shell = getattr(script, class_name)()
-        cmd_map = shell._get_canonical_map()
-        del cmd_map["EOF"]
-
-        # Determine if completing the sub-command name.
-        parts = comp_line[:comp_point].split(None, 1)
-        _log(parts)
-        if len(parts) == 1 or not (' ' in parts[1] or '\t' in parts[1]):
-            #TODO: if parts[1].startswith('-'): handle top-level opts
-            _log("complete sub-command names")
-            matches = {}
-            for name, canon_name in cmd_map.items():
-                if name.startswith(token):
-                    matches[name] = canon_name
-            if not matches:
-                return []
-            elif len(matches) == 1:
-                return matches.keys()
-            elif len(set(matches.values())) == 1:
-                return [matches.values()[0]]
-            else:
-                return matches.keys()
-
-        # Otherwise, complete options for the given sub-command.
-        #TODO: refine this so it does the right thing with option args
-        if token.startswith('-'):
-            cmd_name = comp_line.split(None, 2)[1]
-            try:
-                cmd_canon_name = cmd_map[cmd_name]
-            except KeyError:
-                return []
-            handler = shell._get_cmd_handler(cmd_canon_name)
-            optparser = getattr(handler, "optparser", None)
-            if optparser is None:
-                optparser = SubCmdOptionParser()
-            opt_strs = []
-            for option in optparser.option_list:
-                for opt_str in option._short_opts + option._long_opts:
-                    if opt_str.startswith(token):
-                        opt_strs.append(opt_str)
-            return opt_strs
-
-        return []
-
-    for cpln in _get_bash_cplns(*sys.argv[1:]):
-        print cpln
-
index 1c7ffac..3756dbf 100644 (file)
@@ -1,4 +1,5 @@
 %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+%{!?python_version: %define python_version %(%{__python} -c "import sys; sys.stdout.write(sys.version[:3])")}
 
 %define rc_version 0
 
@@ -22,6 +23,10 @@ Requires:   python-urlgrabber >= 3.9.0
 Requires:   python-xml
 %endif
 
+%if "%{?python_version}" < "2.7"
+Requires:   python-argparse
+%endif
+
 %if 0%{?tizen_version:1}
 Requires:   python-rpm
 %else
index faee633..d74530f 100644 (file)
@@ -16,7 +16,7 @@
 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 from mic import chroot, msger, rt_util
-from mic.utils import cmdln, misc, errors, fs_related
+from mic.utils import misc, errors, fs_related
 from mic.imager import fs
 from mic.conf import configmgr
 from mic.plugin import pluginmgr
@@ -26,12 +26,7 @@ class FsPlugin(ImagerPlugin):
     name = 'fs'
 
     @classmethod
-    @cmdln.option("--include-src",
-                  dest = "include_src",
-                  action = "store_true",
-                  default = False,
-                  help = "Generate a image with source rpms included")
-    def do_create(self, subcmd, opts, *args):
+    def do_create(self, args):
         """${cmd_name}: create fs image
 
         Usage:
@@ -40,11 +35,11 @@ class FsPlugin(ImagerPlugin):
         ${cmd_option_list}
         """
 
-        if len(args) != 1:
-            raise errors.Usage("Extra arguments given")
+        if args is None:
+            raise errors.Usage("Invalid arguments.")
 
         creatoropts = configmgr.create
-        ksconf = args[0]
+        ksconf = args.ksfile
 
         if creatoropts['runtime'] == 'bootstrap':
             configmgr._ksconf = ksconf
@@ -83,7 +78,7 @@ class FsPlugin(ImagerPlugin):
                                        ','.join(backends.keys())))
 
         creator = fs.FsImageCreator(creatoropts, pkgmgr)
-        creator._include_src = opts.include_src
+        creator._include_src = args.include_src
 
         if len(recording_pkgs) > 0:
             creator._recording_pkgs = recording_pkgs
@@ -98,7 +93,7 @@ class FsPlugin(ImagerPlugin):
             creator.mount(None, creatoropts["cachedir"])
             creator.install()
             #Download the source packages ###private options
-            if opts.include_src:
+            if args.include_src:
                 installed_pkgs =  creator.get_installed_packages()
                 msger.info('--------------------------------------------------')
                 msger.info('Generating the image with source rpms included ...')
index f0f2771..1830230 100644 (file)
@@ -20,7 +20,7 @@ import shutil
 import tempfile
 
 from mic import chroot, msger, rt_util
-from mic.utils import misc, fs_related, errors, cmdln
+from mic.utils import misc, fs_related, errors
 from mic.conf import configmgr
 from mic.plugin import pluginmgr
 from mic.imager.loop import LoopImageCreator, load_mountpoints
@@ -30,16 +30,7 @@ class LoopPlugin(ImagerPlugin):
     name = 'loop'
 
     @classmethod
-    @cmdln.option("--compress-disk-image", dest="compress_image",
-                  type='choice', choices=("gz", "bz2"), default=None,
-                  help="Same with --compress-image")
-                  # alias to compress-image for compatibility
-    @cmdln.option("--compress-image", dest="compress_image",
-                  type='choice', choices=("gz", "bz2"), default=None,
-                  help="Compress all loop images with 'gz' or 'bz2'")
-    @cmdln.option("--shrink", action='store_true', default=False,
-                  help="Whether to shrink loop images to minimal size")
-    def do_create(self, subcmd, opts, *args):
+    def do_create(self, args):
         """${cmd_name}: create loop image
 
         Usage:
@@ -48,16 +39,16 @@ class LoopPlugin(ImagerPlugin):
         ${cmd_option_list}
         """
 
-        if len(args) != 1:
-            raise errors.Usage("Extra arguments given")
+        if args is None:
+            raise errors.Usage("Invalid arguments")
 
         creatoropts = configmgr.create
-        ksconf = args[0]
+        ksconf = args.ksfile
 
         if creatoropts['runtime'] == "bootstrap":
             configmgr._ksconf = ksconf
             rt_util.bootstrap_mic()
-        
+
         recording_pkgs = []
         if len(creatoropts['record_pkgs']) > 0:
             recording_pkgs = creatoropts['record_pkgs']
@@ -92,8 +83,8 @@ class LoopPlugin(ImagerPlugin):
 
         creator = LoopImageCreator(creatoropts,
                                    pkgmgr,
-                                   opts.compress_image,
-                                   opts.shrink)
+                                   args.compress_image,
+                                   args.shrink)
 
         if len(recording_pkgs) > 0:
             creator._recording_pkgs = recording_pkgs
@@ -228,8 +219,8 @@ class LoopPlugin(ImagerPlugin):
             raise
 
         try:
-            if len(cmd) != 0:
-                cmdline = ' '.join(cmd)
+            if cmd is not None:
+                cmdline = cmd
             else:
                 cmdline = "/bin/bash"
             envcmd = fs_related.find_binary_inchroot("env", extmnt)
index 17b707c..8acf572 100644 (file)
@@ -64,7 +64,7 @@ class QcowPlugin(ImagerPlugin):
     name = 'qcow'
 
     @classmethod
-    def do_create(cls, subcmd, opts, *args):
+    def do_create(cls, args):
         """${cmd_name}: create qcow image
 
         Usage:
@@ -72,11 +72,11 @@ class QcowPlugin(ImagerPlugin):
 
         ${cmd_option_list}
         """
-        if len(args) != 1:
-            raise errors.Usage("Extra arguments given")
+        if args is None:
+            raise errors.Usage("Invalid arguments")
 
         creatoropts = configmgr.create
-        ksconf = args[0]
+        ksconf = args.ksfile
 
         if creatoropts['runtime'] == "bootstrap":
             configmgr._ksconf = ksconf
@@ -110,7 +110,7 @@ class QcowPlugin(ImagerPlugin):
 
         if not pkgmgr:
             raise errors.CreatorError("Can't find backend: %s, "
-                                      "available choices: %s" %
+                                       "available choices: %s" %
                                       (creatoropts['pkgmgr'],
                                        ','.join(backends.keys())))
 
index 871f9bd..7785bec 100755 (executable)
--- a/tools/mic
+++ b/tools/mic
@@ -30,213 +30,233 @@ import os
 import sys
 import errno
 
-from mic import msger, creator, __version__ as VERSION
-from mic.utils import cmdln, misc, errors
+from argparse import ArgumentParser, SUPPRESS
+
+from mic import msger, __version__ as VERSION
+from mic.utils import misc, errors
 from mic.conf import configmgr
 from mic.plugin import pluginmgr
+from mic.helpformat import MICHelpFormatter, subparser
+    
 
+@subparser
+def chroot_parser(parser):
+    """chroot into an image
 
-def optparser_setup(func):
-    """Setup optparser for a function"""
-    if not hasattr(func, "optparser"):
-        func.optparser = cmdln.SubCmdOptionParser()
-        func.optparser.disable_interspersed_args()
-    return func
-
-
-class MicCmd(cmdln.Cmdln):
+    Examples:
+        mic chroot platform.img
+        mic chroot platform.img ls
     """
-    Usage: mic SUBCOMMAND [OPTS] [ARGS...]
-
-    mic Means the Image Creation tool
-    Try 'mic help SUBCOMMAND' for help on a specific subcommand.
-
-    ${command_list}
-    global ${option_list}
-    ${help_list}
+    parser.add_argument('imagefile', help='Path of image file')
+    parser.add_argument('-s', '--saveto', action = 'store', dest = 'saveto', default = None,
+                        help = "Save the unpacked image to specified dir")
+    parser.add_argument('-c', '--cmd', dest = 'cmd', default = None,
+                        help = "command which will be executed in chroot environment")
+    parser.set_defaults(alias="ch")
+    return parser
+    
+@subparser
+def create_parser(parser):
+    """create an image
+    Examples:
+      $ mic -d -v create auto handset_blackbay.ks
+      $ mic -d -v cr loop handset_blackbay.ks --logfile=mic.log
     """
-    name = 'mic'
-    version = VERSION
 
-    def print_version(self):
+    parent_parser = ArgumentParser(add_help=False)
+    parent_parser.add_argument('ksfile', help='Path of ksfile');
+    parent_parser.add_argument('--logfile', dest='logfile', default=None,
+                               help='Path of logfile')
+    parent_parser.add_argument('-c', '--config', dest='config', default=None,
+                               help='Specify config file for mic')                
+    parent_parser.add_argument('-k', '--cachedir', action='store',
+                               dest='cachedir', default=None,
+                               help='Cache directory to store the downloaded')
+    parent_parser.add_argument('-o', '--outdir', action='store', dest='outdir', 
+                               default=None, help='Output directory')
+    parent_parser.add_argument('-A', '--arch', dest='arch', default=None,
+                               help='Specify repo architecture')
+    parent_parser.add_argument('--release', dest='release', default=None, metavar='RID',
+                               help='Generate a release of RID with all necessary'
+                               ' files, when @BUILD_ID@ is contained in '
+                               'kickstart file, it will be replaced by RID')
+    parent_parser.add_argument("--record-pkgs", dest="record_pkgs", default=None,
+                               help='Record the info of installed packages, '
+                               'multiple values can be specified which '
+                               'joined by ",", valid values: "name", '
+                               '"content", "license", "vcs"')
+    parent_parser.add_argument('--pkgmgr', dest='pkgmgr', default=None,
+                               help='Specify backend package manager')
+    parent_parser.add_argument('--local-pkgs-path', dest='local_pkgs_path', default=None,
+                               help='Path for local pkgs(rpms) to be installed')
+    parent_parser.add_argument('--runtime', dest='runtime', default=None,
+                               help='Specify runtime mode, avaiable: bootstrap')
+    # --taring-to is alias to --pack-to
+    parent_parser.add_argument('--taring-to', dest='pack_to', default=None,
+                               help=SUPPRESS)
+    parent_parser.add_argument('--pack-to', dest='pack_to', default=None,
+                               help='Pack the images together into the specified'
+                                    ' achive, extension supported: .zip, .tar, '
+                                    '.tar.gz, .tar.bz2, etc. by default, .tar '
+                                    'will be used')
+    parent_parser.add_argument('--copy-kernel', action='store_true', dest='copy_kernel',
+                               help='Copy kernel files from image /boot directory'
+                                    ' to the image output directory.')
+    parent_parser.add_argument('--install-pkgs', action='store', dest='install_pkgs', default=None,
+                               help='Specify what type of packages to be installed,'
+                                    ' valid: source, debuginfo, debugsource')
+    parent_parser.add_argument('--check-pkgs', action='store', dest='check_pkgs', default=[],
+                               help='Check if given packages would be installed, '
+                                    'packages should be separated by comma')
+    parent_parser.add_argument('--tmpfs', action='store_true', dest='enabletmpfs',
+                               help='Setup tmpdir as tmpfs to accelerate, experimental'
+                                    ' feature, use it if you have more than 4G memory')
+    parent_parser.add_argument('--repourl', action='append', dest='repourl', default=[],
+                               help=SUPPRESS)
+    parent_parser.add_argument('-R', '--repo', action='append',
+                               dest='repo', default=[],
+                               help=SUPPRESS)
+    parent_parser.add_argument('--ignore-ksrepo', action='store_true',
+                               dest='ignore_ksrepo', default=False,
+                               help=SUPPRESS)
+    parent_parser.add_argument('--strict-mode', action='store_true',
+                               dest='strict_mode', default=False,
+                               help='Abort creation of image, if there are some errors'
+                                    ' during rpm installation. ')
+    parser.set_defaults(alias="cr")
+
+    subparsers  = parser.add_subparsers(title='Subcommands', dest='subcommand')
+    fs_parser   = subparsers.add_parser('fs', parents=[parent_parser], 
+                                        help='auto detect image type from magic header')
+    fs_parser.add_argument("--include-src", dest = "include_src",action = "store_true",
+                           default = False, help = "Generate a image with source rpms included")
+                  
+    auto_parser = subparsers.add_parser('auto', parents=[parent_parser], help='create fs image')
+    loop_parser = subparsers.add_parser('loop', parents=[parent_parser], help='create loop image')
+
+    loop_parser.add_argument("--compress-disk-image", dest="compress_image",
+                             choices=("gz", "bz2", "lzo"), default=None,
+                             help="Same with --compress-image")
+    # alias to compress-image for compatibility
+    loop_parser.add_argument("--compress-image", dest="compress_image",
+                             choices=("gz", "bz2", "lzo"), default=None,
+                             help="Compress all loop images with 'gz' or 'bz2' or 'lzo',"
+                                  "Note: if you want to use 'lzo', package 'lzop' is needed to"
+                                  "be installed manually.")
+    loop_parser.add_argument("--shrink", action='store_true', default=False,
+                  help="Whether to shrink loop images to minimal size")
+                  
+    qcow_parser = subparsers.add_parser('qcow', parents=[parent_parser], help='create qcow image')
+
+    return parser
+
+def main(argv):
+    """Script entry point."""
+    
+    def print_version():
         """log name, verion, hostname"""
-
-        msger.raw("%s %s (%s)" % (self.name,
-                                  self.version,
+        
+        name = 'mic'
+        msger.raw("%s %s (%s)" % (name,
+                                  VERSION,
                                   misc.get_hostname_distro_str()))
-
-    def get_optparser(self):
-        optparser = cmdln.CmdlnOptionParser(self, version=self.version)
-        # hook optparse print_version here
-        optparser.print_version = self.print_version
-        optparser.add_option('-d', '--debug', action='store_true',
-                             dest='debug',
-                             help='print debug message')
-        optparser.add_option('-v', '--verbose', action='store_true',
-                             dest='verbose',
-                             help='verbose information')
-        optparser.add_option('-i', '--interactive', action='store_true',
-                             dest='interactive', default='True',
-                             help='interactive output')
-        optparser.add_option('--non-interactive', action='store_false',
-                             dest='interactive', default='True',
-                             help='non-interactive output')
-        return optparser
-
-    def postoptparse(self):
-        if self.options.interactive:
-            msger.enable_interactive()
-        else:
-            msger.disable_interactive()
-
-        if self.options.verbose:
-            msger.set_loglevel('VERBOSE')
-
-        if self.options.debug:
-            try:
-                import rpm
-                rpm.setVerbosity(rpm.RPMLOG_NOTICE)
-            except ImportError:
-                pass
-
-            msger.set_loglevel('DEBUG')
-
-        self.print_version()
-
-    def help_create(self):
-        """Get help info from doc string.
-           Fill symbols with real parameters
+                                  
+    def has_parameter(arg, arglist):
         """
-        crobj = creator.Creator()
-        crobj.optparser = crobj.get_optparser()
-        doc = crobj.__doc__
-        doc = crobj.help_reindent(doc)
-        doc = crobj.help_preprocess(doc, None)
-        doc = doc.replace(crobj.name, "${cmd_name}", 1)
-        doc = doc.rstrip() + '\n'
-        return doc
-
-    @cmdln.alias("cr")
-    def do_create(self, argv):
-        """Main for creating image"""
-        crobj = creator.Creator()
-        crobj.main(argv[1:])
-
-    def _root_confirm(self):
-        """Make sure command is called by root
-        There are a lot of commands needed to be run during creating images,
-        some of them must be run with root privilege like mount, kpartx"""
-        if os.geteuid() != 0:
-            msger.error('Root permission is required to continue, abort')
-
-    @cmdln.alias("cv")
-    @cmdln.option("-S", "--shell",
-                  action="store_true", dest="shell", default=False,
-                  help="Launch shell before packaging the converted image")
-    def do_convert(self, _subcmd, opts, *args):
-        """${cmd_name}: convert image format
-        Usage:
-            mic convert <imagefile> <destformat>
-
-        ${cmd_option_list}
+        Helper function.
+        Check if argument requires parameter by analyzing
+        its action. Parameter is required only for 'store' and 'append' actions
         """
-        if not args or len(args) != 2:
-            # print help
-            handler = self._get_cmd_handler('convert')
-            if hasattr(handler, "optparser"):
-                handler.optparser.print_help()
-            raise errors.Usage("2 arguments and only 2 are required")
-
-        (srcimg, destformat) = args
-
-        if not os.path.exists(srcimg):
-            raise errors.CreatorError("Cannot find the image: %s" % srcimg)
-
-        self._root_confirm()
-
-        configmgr.convert['shell'] = opts.shell
-
-        srcformat = misc.get_image_type(srcimg)
-        if srcformat == "ext3fsimg":
-            srcformat = "loop"
-
-        srcimager = None
-        destimager = None
-        for iname, icls in pluginmgr.get_plugins('imager').iteritems():
-            if iname == srcformat and hasattr(icls, "do_unpack"):
-                srcimager = icls
-            if iname == destformat and hasattr(icls, "do_pack"):
-                destimager = icls
-
-        if (srcimager and destimager) is None:
-            raise errors.CreatorError("Can't convert from %s to %s" \
-                                  % (srcformat, destformat))
-        maptab = {
-                    "loop": "img",
-                 }
-        if destformat in maptab:
-            imgname = os.path.splitext(os.path.basename(srcimg))[0]
-            dstname = "{0}.{1}".format(imgname, maptab[destformat])
-            if os.path.exists(dstname):
-                if msger.ask("Converted image %s seems existed, "
-                             "remove and continue?" % dstname):
-                    os.unlink(dstname)
-                else:
-                    raise errors.Abort("Canceled")
-
-        destimager.do_pack(srcimager.do_unpack(srcimg))
-
-    @cmdln.alias("ch")
-    @cmdln.option('-s', '--saveto',
-                  action = 'store', dest = 'saveto', default = None,
-                  help = "Save the unpacked image to specified dir")
-    @optparser_setup
-    def do_chroot(self, _subcmd, opts, *args):
-        """${cmd_name}: chroot into an image
-
-        Usage:
-            mic chroot [options] <imagefile> [command [arg]...]
-
-        ${cmd_option_list}
-        """
-        if not args:
-            # print help
-            handler = self._get_cmd_handler('chroot')
-            if hasattr(handler, "optparser"):
-                handler.optparser.print_help()
-            return 1
-
-        targetimage = args[0]
-        if not os.path.exists(targetimage):
-            raise errors.CreatorError("Cannot find the image: %s"
-                                      % targetimage)
-
-        self._root_confirm()
+        if arg.startswith('-'):
+            for args in arglist:
+                if arg in (args['short'], args['long']):
+                    if args.get('action') in (None, 'store', 'append'):
+                        return True
+                    return False
+
+    # Create top level parser
+    epilog = "Try 'mic SUBCOMMAND --help' for help on a specific subcommand."
+    description = "mic - the Image Creation tool"
+    parser = ArgumentParser(description=description, epilog=epilog,
+                            formatter_class=MICHelpFormatter)
+
+    # List of global arguments
+    # The main purpose of this structure is to contain arguments
+    # of add_argument. This is used to do aliasing properly
+    # (see code under the comment 'replace aliases with real commands')
+    global_args = [{'short': '-V', 'long': '--version', 'action': 'version',
+                    'version': '%(prog)s ' + VERSION},
+                   {'short': '-d', 'long': '--debug', 'action': 'store_true',
+                    'help': 'debug output'},
+                   {'short': '-v', 'long': '--verbose', 'action': 'store_true',
+                    'help': 'verbose output'},
+                   {'short': '-i', 'long': '--interactive', 'action': 'store_true',
+                    'dest': 'interactive', 'default': 'True', 'help': 'interactive output'}, 
+                   {'short': '', 'long': '--non-interactive', 'action': 'store_false',
+                    'dest': 'interactive', 'default': 'True', 'help': 'non-interactive output'}, 
+                  ]
+
+    for args in global_args:
+        parser_kwargs = {}
+        for key in ('action', 'help', 'version', 'default', 'dest'):
+            if key in args:
+                parser_kwargs[key] = args[key]
+        
+        if args['short'] is '':
+            parser.add_argument(args['long'], **parser_kwargs)
+        else:
+            parser.add_argument(args['short'], args['long'], **parser_kwargs)
+
+    # hacked by the request of cmdln lovers
+    parser.format_usage = parser.format_help
+
+    # Create parsers for subcommands
+    subparsers = parser.add_subparsers(title='subcommands')
+
+    # collect aliases
+    aliases = {}
+    for name, obj in globals().iteritems():
+        if name.endswith('_parser') and callable(obj):
+            aliases[obj(subparsers).get_default('alias')] = name.split('_')[0]
+
+    # replace aliases with real commands
+    for i, arg in enumerate(argv[1:]):
+        if not arg.startswith('-'):
+            # argv[i] is previous argument to arg
+            if not has_parameter(argv[i], global_args) and arg in aliases:
+                argv[i+1] = aliases[arg]
+                break
+    
+    # Parse arguments
+    args = parser.parse_args(argv[1:])
 
-        configmgr.chroot['saveto'] = opts.saveto
+    if args.interactive:
+        msger.enable_interactive()
+    else:
+        msger.disable_interactive()
 
-        imagetype = misc.get_image_type(targetimage)
-        if imagetype in ("ext3fsimg", "ext4fsimg", "btrfsimg"):
-            imagetype = "loop"
+    if args.verbose:
+        msger.set_loglevel('VERBOSE')
 
-        chrootclass = None
-        for pname, pcls in pluginmgr.get_plugins('imager').iteritems():
-            if pname == imagetype and hasattr(pcls, "do_chroot"):
-                chrootclass = pcls
-                break
+    if args.debug:
+        try:
+            import rpm
+            rpm.setVerbosity(rpm.RPMLOG_NOTICE)
+        except ImportError:
+            pass
 
-        if not chrootclass:
-            raise errors.CreatorError("Cannot support image type: %s" \
-                                      % imagetype)
+        msger.set_loglevel('DEBUG')
 
-        chrootclass.do_chroot(targetimage, args[1:])
+    print_version()
 
+    # Import target module and call 'main' from it
+    module = __import__("mic.%s" % args.module, fromlist=[args.module])
+    return module.main(parser, args, argv[1:])
 
+    
 if __name__ == "__main__":
     try:
-        MIC = MicCmd()
-        sys.exit(MIC.main())
+        sys.exit(main(sys.argv))
     except KeyboardInterrupt:
         msger.error('\n^C catched, program aborted.')
     except IOError as ioerr: