1 # Copyright(c) 2009, Gentoo Foundation
3 # Licensed under the GNU General Public License, v2 or higher
7 """List installed packages matching the query pattern"""
9 from __future__ import print_function
11 __docformat__ = 'epytext'
18 from getopt import gnu_getopt, GetoptError
21 import gentoolkit.pprinter as pp
22 from gentoolkit.equery import format_options, mod_usage, CONFIG
23 from gentoolkit.helpers import get_installed_cpvs
24 from gentoolkit.package import PackageFormatter, FORMAT_TMPL_VARS
25 from gentoolkit.query import Query
36 "include_mask_reason": False,
38 "show_progress": (not CONFIG['quiet']),
39 "package_format": None
46 def print_help(with_description=True):
47 """Print description, usage and a detailed help message.
49 @type with_description: bool
50 @param with_description: if true, print module's __doc__ string
54 print(__doc__.strip())
57 # Deprecation warning added by djanderson, 12/2008
59 "Default action for this module has changed in Gentoolkit 0.3.",
60 "Use globbing to simulate the old behavior (see man equery).",
61 "Use '*' to check all installed packages.",
62 "Use 'foo-bar/*' to filter by category."
64 for line in depwarning:
65 sys.stderr.write(pp.warn(line))
68 print(mod_usage(mod_name="list"))
70 print(pp.command("options"))
71 print(format_options((
72 (" -h, --help", "display this help message"),
73 (" -d, --duplicates", "list only installed duplicate packages"),
74 (" -f, --full-regex", "query is a regular expression"),
75 (" -m, --mask-reason", "include reason for package mask"),
76 (" -I, --exclude-installed",
77 "exclude installed packages from output"),
78 (" -o, --overlay-tree", "list packages in overlays"),
79 (" -p, --portage-tree", "list packages in the main portage tree"),
80 (" -F, --format=TMPL", "specify a custom output format"),
82 "a format template using (see man page):")
84 print(" " * 24, ', '.join(pp.emph(x) for x in FORMAT_TMPL_VARS))
87 def get_duplicates(matches):
88 """Return only packages that have more than one version installed."""
94 dups[pkg.cp].append(pkg)
98 for cpv in dups.values():
105 def parse_module_options(module_opts):
106 """Parse module options and update QUERY_OPTS"""
108 opts = (x[0] for x in module_opts)
109 posargs = (x[1] for x in module_opts)
110 for opt, posarg in zip(opts, posargs):
111 if opt in ('-h', '--help'):
114 elif opt in ('-I', '--exclude-installed'):
115 QUERY_OPTS['in_installed'] = False
116 elif opt in ('-p', '--portage-tree'):
117 QUERY_OPTS['in_porttree'] = True
118 elif opt in ('-o', '--overlay-tree'):
119 QUERY_OPTS['in_overlay'] = True
120 elif opt in ('-f', '--full-regex'):
121 QUERY_OPTS['is_regex'] = True
122 elif opt in ('-m', '--mask-reason'):
123 QUERY_OPTS['include_mask_reason'] = True
124 elif opt in ('-e', '--exact-name'):
125 sys.stderr.write(pp.warn("-e, --exact-name is now default."))
127 pp.warn("Use globbing to simulate the old behavior.")
130 elif opt in ('-d', '--duplicates'):
131 QUERY_OPTS['duplicates'] = True
132 elif opt in ('-F', '--format'):
133 QUERY_OPTS["package_format"] = posarg
136 def main(input_args):
137 """Parse input and run the program"""
139 short_opts = "hdefiImopF:" # -i, -e were options for default actions
142 # --all is no longer needed. Kept for compatibility.
143 # --installed is no longer needed. Kept for compatibility.
144 # --exact-name is no longer needed. Kept for compatibility.
145 long_opts = ('help', 'all', 'installed', 'exclude-installed',
146 'mask-reason', 'portage-tree', 'overlay-tree', 'format=', 'full-regex',
147 'exact-name', 'duplicates')
150 module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
151 except GetoptError as err:
152 sys.stderr.write(pp.error("Module %s" % err))
154 print_help(with_description=False)
157 parse_module_options(module_opts)
159 # Only search installed packages when listing duplicate packages
160 if QUERY_OPTS["duplicates"]:
161 QUERY_OPTS["in_installed"] = True
162 QUERY_OPTS["in_porttree"] = False
163 QUERY_OPTS["in_overlay"] = False
164 QUERY_OPTS["include_mask_reason"] = False
171 for query in (Query(x, QUERY_OPTS['is_regex']) for x in queries):
175 matches = query.smart_find(**QUERY_OPTS)
177 # Find duplicate packages
178 if QUERY_OPTS["duplicates"]:
179 matches = get_duplicates(matches)
188 pkgstr = PackageFormatter(
190 do_format=CONFIG['verbose'],
191 custom_format=QUERY_OPTS["package_format"]
194 if (QUERY_OPTS["in_porttree"] and
195 not QUERY_OPTS["in_overlay"]):
196 if not 'P' in pkgstr.location:
198 if (QUERY_OPTS["in_overlay"] and
199 not QUERY_OPTS["in_porttree"]):
200 if not 'O' in pkgstr.location:
204 if QUERY_OPTS["include_mask_reason"]:
205 ms_int, ms_orig = pkgstr.format_mask_status()
207 # ms_int is a number representation of mask level.
208 # Only 2 and above are "hard masked" and have reasons.
210 mask_reason = pkg.mask_reason()
212 # Package not on system or not masked
214 elif not any(mask_reason):
215 print(" * No mask reason given")
217 status = ', '.join(ms_orig)
218 explanation = mask_reason[0]
219 mask_location = mask_reason[1]
220 pp.uprint(" * Masked by %r" % status)
221 pp.uprint(" * %s:" % mask_location)
223 [' * %s' % line.lstrip(' #')
224 for line in explanation.splitlines()]
229 # vim: set ts=4 sw=4 tw=79: