--- a/src/client.py Thu Apr 23 15:55:54 2015 -0700
+++ b/src/client.py Fri Apr 24 14:42:07 2015 -0700
@@ -143,7 +143,8 @@
# program name on all platforms.
logger.error(ws + pkg_cmd + text_nows)
-def usage(usage_error=None, cmd=None, retcode=EXIT_BADOPT, full=False):
+def usage(usage_error=None, cmd=None, retcode=EXIT_BADOPT, full=False,
+ verbose=False, unknown_cmd=None):
"""Emit a usage message and optionally prefix it with a more
specific error message. Causes program to exit. """
@@ -397,27 +398,57 @@
sys.exit(retcode)
elif not full:
- # The full usage message isn't desired.
- logger.error(_("Try `pkg --help or -?' for more information."))
+ # The full list of subcommands isn't desired.
+ known_words = ["help"]
+ known_words.extend(basic_cmds)
+ known_words.extend(w for w in advanced_cmds if w)
+ candidates = misc.suggest_known_words(unknown_cmd, known_words)
+ if candidates:
+ # Suggest correct subcommands if we can.
+ words = ", ". join(candidates)
+ logger.error(_("Did you mean:\n {0}\n").format(words))
+ logger.error(_("For a full list of subcommands, run: pkg help"))
sys.exit(retcode)
- logger.error(_("""\
+ if verbose:
+ # Display a verbose usage message of subcommands.
+ logger.error(_("""\
Usage:
pkg [options] command [cmd_options] [operands]
"""))
- logger.error(_("Basic subcommands:"))
- print_cmds(basic_cmds, basic_usage)
-
- logger.error(_("\nAdvanced subcommands:"))
- print_cmds(advanced_cmds, adv_usage)
-
- logger.error(_("""
+ logger.error(_("Basic subcommands:"))
+ print_cmds(basic_cmds, basic_usage)
+
+ logger.error(_("\nAdvanced subcommands:"))
+ print_cmds(advanced_cmds, adv_usage)
+
+ logger.error(_("""
Options:
-R dir
--help or -?
Environment:
PKG_IMAGE"""))
+ else:
+ # Display the full list of subcommands.
+ logger.error(_("""\
+Usage: pkg [options] command [cmd_options] [operands]"""))
+ logger.error(_("The following commands are supported:"))
+ logger.error(_("""
+Package Information : list search info contents
+Package Transitions : update install uninstall
+ history exact-install
+Package Maintenance : verify fix revert
+Publishers : publisher set-publisher unset-publisher
+Package Configuration: mediator set-mediator unset-mediator
+ facet change-facet
+ variant change-variant
+Image Constraints : avoid unavoid freeze unfreeze
+Image Configuration : refresh rebuild-index purge-history
+ property set-property add-property-value
+ unset-property remove-property-value
+Miscellaneous : image-create dehydrate rehydrate
+For more info, run: pkg help <command>"""))
sys.exit(retcode)
def get_fmri_args(api_inst, pargs, cmd=None):
@@ -5196,9 +5227,14 @@
if sub in cmds and \
sub not in ["help", "-?", "--help"]:
usage(retcode=0, full=False, cmd=sub)
+ elif sub == "-v":
+ # Only display the long usage message
+ # in the verbose mode.
+ usage(retcode=0, full=True,
+ verbose=True)
elif sub not in ["help", "-?", "--help"]:
usage(_("unknown subcommand "
- "'{0}'").format(sub), full=True)
+ "'{0}'").format(sub), unknown_cmd=sub)
else:
usage(retcode=0, full=True)
else:
@@ -5209,11 +5245,11 @@
usage(retcode=0, cmd=subcommand, full=False)
if subcommand and subcommand not in cmds:
usage(_("unknown subcommand '{0}'").format(subcommand),
- full=True)
+ unknown_cmd=subcommand)
if show_usage:
usage(retcode=0, full=True)
if not subcommand:
- usage(_("no subcommand specified"))
+ usage(_("no subcommand specified"), full=True)
if runid is not None:
try:
runid = int(runid)
--- a/src/man/pkg.1 Thu Apr 23 15:55:54 2015 -0700
+++ b/src/man/pkg.1 Fri Apr 24 14:42:07 2015 -0700
@@ -7,11 +7,11 @@
<refentry id="pkg-1">
<refmeta><refentrytitle>pkg</refentrytitle><manvolnum>1</manvolnum>
-<refmiscinfo class="date">12 Nov 2014</refmiscinfo>
+<refmiscinfo class="date">22 Apr 2015</refmiscinfo>
<refmiscinfo class="sectdesc">&man1;</refmiscinfo>
<refmiscinfo class="software">&release;</refmiscinfo>
<refmiscinfo class="arch">generic</refmiscinfo>
-<refmiscinfo class="copyright">Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.</refmiscinfo>
+<refmiscinfo class="copyright">Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.</refmiscinfo>
</refmeta>
<refnamediv>
<refname>pkg</refname><refpurpose>Image Packaging System retrieval client</refpurpose>
@@ -152,7 +152,7 @@
<synopsis>/usr/bin/pkg rebuild-index</synopsis>
<synopsis>/usr/bin/pkg update-format</synopsis>
<synopsis>/usr/bin/pkg version</synopsis>
-<synopsis>/usr/bin/pkg help</synopsis>
+<synopsis>/usr/bin/pkg help [-v]</synopsis>
<synopsis>/usr/bin/pkg image-create [-FPUfz] [--force]
[--full | --partial | --user] [--zone]
[-c <replaceable>ssl_cert</replaceable>] [-k <replaceable>ssl_key</replaceable>]
@@ -1650,9 +1650,13 @@
This string is not guaranteed to be comparable in any fashion between versions.</para>
</listitem>
</varlistentry>
-<varlistentry><term><command>pkg help</command></term>
-<listitem><para>Display a usage message.</para>
+<varlistentry><term><command>pkg help</command> [<option>v</option>]</term>
+<listitem><para>Display a full list of subcommands.</para>
</listitem>
+<listitem><varlistentry><term><option>v</option></term>
+<listitem><para>Display a verbose usage message of subcommands.</para>
+</listitem>
+</varlistentry></listitem>
</varlistentry>
<varlistentry><term><command>pkg image-create</command> [<option>FPUfz</option>] [<option>-force</option>] [<option>-full</option> | <option>-partial</option> | <option>-user</option>] [<option>-zone</option>] [<option>c</option> <replaceable>ssl_cert</replaceable>] [<option>k</option> <replaceable> ssl_key</replaceable>] [<option>g</option> <replaceable>path_or_uri</replaceable> | <option>-origin</option> <replaceable>path_or_uri</replaceable>]... [<option>m</option> <replaceable>uri</replaceable> | <option>-mirror</option> <replaceable>uri</replaceable>]... [<option>-facet</option> <replaceable>facet_name</replaceable>=(<literal>True</literal>|<literal>False</literal>)]... [<option>-no-refresh</option>] [<option>-set-property</option> <replaceable>name_of_property</replaceable>=<replaceable>value</replaceable>] [<option>-variant</option> <replaceable>variant_name</replaceable>=<replaceable>value</replaceable>]... [(<option>p</option> | <option>-publisher</option>) [<replaceable>name</replaceable>=]<replaceable>repo_uri</replaceable>] <replaceable>dir</replaceable></term>
<listitem><para>At the location given by <replaceable>dir</replaceable>, create an image suitable for package operations. Images created by using the <command>image-create</command> subcommand are not bootable. Most users should use the <option>-be-name</option> or <option>-require-new-be</option> options with <command>pkg</command> commands, or use the <command>beadm</command> or <command>zoneadm</command> commands to create images. The <command>pkg image-create</command> command is used for tasks such as maintaining packages and operating system distributions.</para>
--- a/src/modules/misc.py Thu Apr 23 15:55:54 2015 -0700
+++ b/src/modules/misc.py Fri Apr 24 14:42:07 2015 -0700
@@ -57,6 +57,7 @@
import zlib
from collections import defaultdict
+from operator import itemgetter
from stat import S_IFMT, S_IMODE, S_IRGRP, S_IROTH, S_IRUSR, S_IRWXU, \
S_ISBLK, S_ISCHR, S_ISDIR, S_ISFIFO, S_ISLNK, S_ISREG, S_ISSOCK, \
@@ -2764,3 +2765,70 @@
last_line = line
yield line
+def _min_edit_distance(word1, word2):
+ """Calculate the minimal edit distance for converting word1 to word2,
+ based on Wagner-Fischer algorithm."""
+
+ m = len(word1)
+ n = len(word2)
+
+ # dp[i][j] stands for the edit distance between two strings with
+ # length i and j, i.e., word1[0,...,i-1] and word2[0,...,j-1]
+ dp = [[0 for i in range(n+1)] for j in range(m+1)]
+
+ ins_cost = 1.0
+ del_cost = 1.0
+ rep_cost = 1.0
+ for i in range(m+1):
+ dp[i][0] = del_cost * i
+ for i in range(n+1):
+ dp[0][i] = ins_cost * i
+
+ for i in range(1, m+1):
+ for j in range(1, n+1):
+ if word1[i-1] == word2[j-1]:
+ dp[i][j] = dp[i-1][j-1]
+ else:
+ dp[i][j] = min(
+ dp[i-1][j-1] + rep_cost,
+ dp[i][j-1] + ins_cost,
+ dp[i-1][j] + del_cost)
+
+ return dp[m][n]
+
+def suggest_known_words(text, known_words):
+ """Given a text, a list of known_words, suggest some correct
+ candidates from known_words."""
+
+ candidates = []
+ if not text:
+ return candidates
+
+ # We are confident to suggest if the text is part of the known words.
+ for known in known_words:
+ if len(text) < 4:
+ # If the text's length is short, treat it as a prefix.
+ if known.startswith(text):
+ candidates.append(known)
+ elif text in known or known in text:
+ # Otherwise check if the text is part of the known
+ # words or vice verse.
+ candidates.append(known)
+
+ if candidates:
+ if len(candidates) < 4:
+ return candidates
+ else:
+ # Give up suggestions if there are too many candidates.
+ return
+
+ # If there are no candidates from the "contains" check, use the edit
+ # distance algorithm to seek further.
+ for known in known_words:
+ distance = _min_edit_distance(text, known)
+ if distance <= len(known) / 2.0:
+ candidates.append((known, distance))
+
+ # Sort the candidates by their distance, and return the words only.
+ return [c[0] for c in sorted(candidates, key=itemgetter(1))]
+
--- a/src/tests/cli/t_pkg_help.py Thu Apr 23 15:55:54 2015 -0700
+++ b/src/tests/cli/t_pkg_help.py Fri Apr 24 14:42:07 2015 -0700
@@ -54,8 +54,15 @@
self.assert_(False, "{0} in {1}".format(
str, msg))
+ # Full list of subcommands, ensuring we exit 0
+ for option in ["-\?", "--help", "help"]:
+ ret, out, err = self.pkg(option, out=True, stderr=True)
+ verify_help(err,
+ ["pkg [options] command [cmd_options] [operands]",
+ "For more info, run: pkg help <command>"])
+
# Full usage text, ensuring we exit 0
- for option in ["-\?", "--help", "help"]:
+ for option in ["help -v"]:
ret, out, err = self.pkg(option, out=True, stderr=True)
verify_help(err,
["pkg [options] command [cmd_options] [operands]",
@@ -68,16 +75,16 @@
ret, out, err = self.pkg("-\? bobcat", exit=2, out=True,
stderr=True)
verify_help(err,
- ["pkg [options] command [cmd_options] [operands]",
- "pkg: unknown subcommand",
- "PKG_IMAGE", "Usage:"])
+ ["pkg: unknown subcommand",
+ "For a full list of subcommands, run: pkg help"])
# Unrequested usage
ret, out, err = self.pkg("", exit=2, out=True, stderr=True)
verify_help(err,
["pkg: no subcommand specified",
- "Try `pkg --help or -?' for more information."],
- unexpected = ["PKG_IMAGE", "Usage:"])
+ "pkg [options] command [cmd_options] [operands]",
+ "For more info, run: pkg help <command>"],
+ unexpected = ["PKG_IMAGE"])
# help for a subcommand should only print that subcommand usage
for option in ["property --help", "--help property",
@@ -113,7 +120,7 @@
f = codecs.open(eucJP_encode_file, encoding="eucJP")
locale_env = { "LC_ALL": "ja_JP.eucJP" }
- ret, out, err = self.pkg("--help", env_arg=locale_env,
+ ret, out, err = self.pkg("help -v", env_arg=locale_env,
out=True, stderr=True)
cmd_out = unicode(err, encoding="eucJP")
# Take only 4 lines from "pkg --help" command output.