portageq: add aliases for bug #476830
[portage.git] / bin / portageq
1 #!/usr/bin/python -O
2 # Copyright 1999-2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 from __future__ import print_function, unicode_literals
6
7 import signal
8 import sys
9 # This block ensures that ^C interrupts are handled quietly.
10 try:
11
12         def exithandler(signum, frame):
13                 signal.signal(signal.SIGINT, signal.SIG_IGN)
14                 signal.signal(signal.SIGTERM, signal.SIG_IGN)
15                 sys.exit(128 + signum)
16
17         signal.signal(signal.SIGINT, exithandler)
18         signal.signal(signal.SIGTERM, exithandler)
19
20 except KeyboardInterrupt:
21         sys.exit(128 + signal.SIGINT)
22
23 import optparse
24 import os
25 import types
26
27 # Avoid sandbox violations after python upgrade.
28 pym_path = os.path.join(os.path.dirname(
29         os.path.dirname(os.path.realpath(__file__))), "pym")
30 if os.environ.get("SANDBOX_ON") == "1":
31         sandbox_write = os.environ.get("SANDBOX_WRITE", "").split(":")
32         if pym_path not in sandbox_write:
33                 sandbox_write.append(pym_path)
34                 os.environ["SANDBOX_WRITE"] = \
35                         ":".join(filter(None, sandbox_write))
36         del sandbox_write
37
38 sys.path.insert(0, pym_path)
39 import portage
40 portage._internal_caller = True
41 from portage import os
42 from portage.eapi import eapi_has_repo_deps
43 from portage.util import writemsg, writemsg_stdout
44 portage.proxy.lazyimport.lazyimport(globals(),
45         're',
46         'subprocess',
47         '_emerge.Package:Package',
48         '_emerge.RootConfig:RootConfig',
49         '_emerge.is_valid_package_atom:insert_category_into_atom',
50         'portage.dbapi._expand_new_virt:expand_new_virt',
51         'portage._sets.base:InternalPackageSet',
52         'portage.xml.metadata:MetaDataXML'
53 )
54
55 def eval_atom_use(atom):
56         if 'USE' in os.environ:
57                 use = frozenset(os.environ['USE'].split())
58                 atom = atom.evaluate_conditionals(use)
59         return atom
60
61 def uses_eroot(function):
62         function.uses_eroot = True
63         return function
64
65 #-----------------------------------------------------------------------------
66 #
67 # To add functionality to this tool, add a function below.
68 #
69 # The format for functions is:
70 #
71 #   def function(argv):
72 #       """<list of options for this function>
73 #       <description of the function>
74 #       """
75 #       <code>
76 #
77 # "argv" is an array of the command line parameters provided after the command.
78 #
79 # Make sure you document the function in the right format.  The documentation
80 # is used to display help on the function.
81 #
82 # You do not need to add the function to any lists, this tool is introspective,
83 # and will automaticly add a command by the same name as the function!
84 #
85
86 @uses_eroot
87 def has_version(argv):
88         """<eroot> <category/package>
89         Return code 0 if it's available, 1 otherwise.
90         """
91         if (len(argv) < 2):
92                 print("ERROR: insufficient parameters!")
93                 return 3
94
95         warnings = []
96
97         allow_repo = atom_validate_strict is False or eapi_has_repo_deps(eapi)
98         try:
99                 atom = portage.dep.Atom(argv[1], allow_repo=allow_repo)
100         except portage.exception.InvalidAtom:
101                 if atom_validate_strict:
102                         portage.writemsg("ERROR: Invalid atom: '%s'\n" % argv[1],
103                                 noiselevel=-1)
104                         return 2
105                 else:
106                         atom = argv[1]
107         else:
108                 if atom_validate_strict:
109                         try:
110                                 atom = portage.dep.Atom(argv[1], allow_repo=allow_repo, eapi=eapi)
111                         except portage.exception.InvalidAtom as e:
112                                 warnings.append("QA Notice: %s: %s" % ('has_version', e))
113                 atom = eval_atom_use(atom)
114
115         if warnings:
116                 elog('eqawarn', warnings)
117
118         try:
119                 mylist = portage.db[argv[0]]["vartree"].dbapi.match(atom)
120                 if mylist:
121                         return 0
122                 else:
123                         return 1
124         except KeyError:
125                 return 1
126         except portage.exception.InvalidAtom:
127                 portage.writemsg("ERROR: Invalid atom: '%s'\n" % argv[1],
128                         noiselevel=-1)
129                 return 2
130
131
132 @uses_eroot
133 def best_version(argv):
134         """<eroot> <category/package>
135         Returns category/package-version (without .ebuild).
136         """
137         if (len(argv) < 2):
138                 print("ERROR: insufficient parameters!")
139                 return 3
140
141         warnings = []
142
143         allow_repo = atom_validate_strict is False or eapi_has_repo_deps(eapi)
144         try:
145                 atom = portage.dep.Atom(argv[1], allow_repo=allow_repo)
146         except portage.exception.InvalidAtom:
147                 if atom_validate_strict:
148                         portage.writemsg("ERROR: Invalid atom: '%s'\n" % argv[1],
149                                 noiselevel=-1)
150                         return 2
151                 else:
152                         atom = argv[1]
153         else:
154                 if atom_validate_strict:
155                         try:
156                                 atom = portage.dep.Atom(argv[1], allow_repo=allow_repo, eapi=eapi)
157                         except portage.exception.InvalidAtom as e:
158                                 warnings.append("QA Notice: %s: %s" % ('best_version', e))
159                 atom = eval_atom_use(atom)
160
161         if warnings:
162                 elog('eqawarn', warnings)
163
164         try:
165                 mylist = portage.db[argv[0]]["vartree"].dbapi.match(atom)
166                 print(portage.best(mylist))
167         except KeyError:
168                 return 1
169
170
171 @uses_eroot
172 def mass_best_version(argv):
173         """<eroot> [<category/package>]+
174         Returns category/package-version (without .ebuild).
175         """
176         if (len(argv) < 2):
177                 print("ERROR: insufficient parameters!")
178                 return 2
179         try:
180                 for pack in argv[1:]:
181                         mylist=portage.db[argv[0]]["vartree"].dbapi.match(pack)
182                         print(pack+":"+portage.best(mylist))
183         except KeyError:
184                 return 1
185
186
187 @uses_eroot
188 def metadata(argv):
189         if (len(argv) < 4):
190                 print("ERROR: insufficient parameters!", file=sys.stderr)
191                 return 2
192
193         eroot, pkgtype, pkgspec = argv[0:3]
194         metakeys = argv[3:]
195         type_map = {
196                 "ebuild":"porttree",
197                 "binary":"bintree",
198                 "installed":"vartree"}
199         if pkgtype not in type_map:
200                 print("Unrecognized package type: '%s'" % pkgtype, file=sys.stderr)
201                 return 1
202         trees = portage.db
203         repo = portage.dep.dep_getrepo(pkgspec)
204         pkgspec = portage.dep.remove_slot(pkgspec)
205         try:
206                         values = trees[eroot][type_map[pkgtype]].dbapi.aux_get(
207                                 pkgspec, metakeys, myrepo=repo)
208                         writemsg_stdout(''.join('%s\n' % x for x in values), noiselevel=-1)
209         except KeyError:
210                 print("Package not found: '%s'" % pkgspec, file=sys.stderr)
211                 return 1
212
213 metadata.__doc__ = """
214 <eroot> <pkgtype> <category/package> [<key>]+
215 Returns metadata values for the specified package.
216 Available keys: %s
217 """  % ','.join(sorted(x for x in portage.auxdbkeys \
218 if not x.startswith('UNUSED_')))
219
220
221 @uses_eroot
222 def contents(argv):
223         """<eroot> <category/package>
224         List the files that are installed for a given package, with
225         one file listed on each line. All file names will begin with
226         <eroot>.
227         """
228         if len(argv) != 2:
229                 print("ERROR: expected 2 parameters, got %d!" % len(argv))
230                 return 2
231
232         root, cpv = argv
233         vartree = portage.db[root]["vartree"]
234         if not vartree.dbapi.cpv_exists(cpv):
235                 sys.stderr.write("Package not found: '%s'\n" % cpv)
236                 return 1
237         cat, pkg = portage.catsplit(cpv)
238         db = portage.dblink(cat, pkg, root, vartree.settings,
239                 treetype="vartree", vartree=vartree)
240         writemsg_stdout(''.join('%s\n' % x for x in sorted(db.getcontents())),
241                 noiselevel=-1)
242
243
244 @uses_eroot
245 def owners(argv):
246         """<eroot> [<filename>]+
247         Given a list of files, print the packages that own the files and which
248         files belong to each package. Files owned by a package are listed on
249         the lines below it, indented by a single tab character (\\t). All file
250         paths must either start with <eroot> or be a basename alone.
251         Returns 1 if no owners could be found, and 0 otherwise.
252         """
253         if len(argv) < 2:
254                 sys.stderr.write("ERROR: insufficient parameters!\n")
255                 sys.stderr.flush()
256                 return 2
257
258         eroot = argv[0]
259         vardb = portage.db[eroot]["vartree"].dbapi
260         root = portage.settings['ROOT']
261
262         cwd = None
263         try:
264                 cwd = os.getcwd()
265         except OSError:
266                 pass
267
268         files = []
269         orphan_abs_paths = set()
270         orphan_basenames = set()
271         for f in argv[1:]:
272                 f = portage.normalize_path(f)
273                 is_basename = os.sep not in f
274                 if not is_basename and f[:1] != os.sep:
275                         if cwd is None:
276                                 sys.stderr.write("ERROR: cwd does not exist!\n")
277                                 sys.stderr.flush()
278                                 return 2
279                         f = os.path.join(cwd, f)
280                         f = portage.normalize_path(f)
281                 if not is_basename and not f.startswith(eroot):
282                         sys.stderr.write("ERROR: file paths must begin with <eroot>!\n")
283                         sys.stderr.flush()
284                         return 2
285                 if is_basename:
286                         files.append(f)
287                         orphan_basenames.add(f)
288                 else:
289                         files.append(f[len(root)-1:])
290                         orphan_abs_paths.add(f)
291
292         owners = vardb._owners.get_owners(files)
293
294         msg = []
295         for pkg, owned_files in owners.items():
296                 cpv = pkg.mycpv
297                 msg.append("%s\n" % cpv)
298                 for f in sorted(owned_files):
299                         f_abs = os.path.join(root, f.lstrip(os.path.sep))
300                         msg.append("\t%s\n" % (f_abs,))
301                         orphan_abs_paths.discard(f_abs)
302                         if orphan_basenames:
303                                 orphan_basenames.discard(os.path.basename(f_abs))
304
305         writemsg_stdout(''.join(msg), noiselevel=-1)
306
307         if orphan_abs_paths or orphan_basenames:
308                 orphans = []
309                 orphans.extend(orphan_abs_paths)
310                 orphans.extend(orphan_basenames)
311                 orphans.sort()
312                 msg = []
313                 msg.append("None of the installed packages claim these files:\n")
314                 for f in orphans:
315                         msg.append("\t%s\n" % (f,))
316                 sys.stderr.write("".join(msg))
317                 sys.stderr.flush()
318
319         if owners:
320                 return 0
321         return 1
322
323
324 @uses_eroot
325 def is_protected(argv):
326         """<eroot> <filename>
327         Given a single filename, return code 0 if it's protected, 1 otherwise.
328         The filename must begin with <eroot>.
329         """
330         if len(argv) != 2:
331                 sys.stderr.write("ERROR: expected 2 parameters, got %d!\n" % len(argv))
332                 sys.stderr.flush()
333                 return 2
334
335         root, filename = argv
336
337         err = sys.stderr
338         cwd = None
339         try:
340                 cwd = os.getcwd()
341         except OSError:
342                 pass
343
344         f = portage.normalize_path(filename)
345         if not f.startswith(os.path.sep):
346                 if cwd is None:
347                         err.write("ERROR: cwd does not exist!\n")
348                         err.flush()
349                         return 2
350                 f = os.path.join(cwd, f)
351                 f = portage.normalize_path(f)
352
353         if not f.startswith(root):
354                 err.write("ERROR: file paths must begin with <eroot>!\n")
355                 err.flush()
356                 return 2
357
358         from portage.util import ConfigProtect
359
360         settings = portage.settings
361         protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
362         protect_mask = portage.util.shlex_split(
363                 settings.get("CONFIG_PROTECT_MASK", ""))
364         protect_obj = ConfigProtect(root, protect, protect_mask)
365
366         if protect_obj.isprotected(f):
367                 return 0
368         return 1
369
370
371 @uses_eroot
372 def filter_protected(argv):
373         """<eroot>
374         Read filenames from stdin and write them to stdout if they are protected.
375         All filenames are delimited by \\n and must begin with <eroot>.
376         """
377         if len(argv) != 1:
378                 sys.stderr.write("ERROR: expected 1 parameter, got %d!\n" % len(argv))
379                 sys.stderr.flush()
380                 return 2
381
382         root, = argv
383         out = sys.stdout
384         err = sys.stderr
385         cwd = None
386         try:
387                 cwd = os.getcwd()
388         except OSError:
389                 pass
390
391         from portage.util import ConfigProtect
392
393         settings = portage.settings
394         protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
395         protect_mask = portage.util.shlex_split(
396                 settings.get("CONFIG_PROTECT_MASK", ""))
397         protect_obj = ConfigProtect(root, protect, protect_mask)
398
399         protected = 0
400         errors = 0
401
402         for line in sys.stdin:
403                 filename = line.rstrip("\n")
404                 f = portage.normalize_path(filename)
405                 if not f.startswith(os.path.sep):
406                         if cwd is None:
407                                 err.write("ERROR: cwd does not exist!\n")
408                                 err.flush()
409                                 errors += 1
410                                 continue
411                         f = os.path.join(cwd, f)
412                         f = portage.normalize_path(f)
413
414                 if not f.startswith(root):
415                         err.write("ERROR: file paths must begin with <eroot>!\n")
416                         err.flush()
417                         errors += 1
418                         continue
419
420                 if protect_obj.isprotected(f):
421                         protected += 1
422                         out.write("%s\n" % filename)
423         out.flush()
424
425         if errors:
426                 return 2
427
428         return 0
429
430
431 @uses_eroot
432 def best_visible(argv):
433         """<eroot> [pkgtype] <atom>
434         Returns category/package-version (without .ebuild).
435         The pkgtype argument defaults to "ebuild" if unspecified,
436         otherwise it must be one of ebuild, binary, or installed.
437         """
438         if (len(argv) < 2):
439                 writemsg("ERROR: insufficient parameters!\n", noiselevel=-1)
440                 return 2
441
442         pkgtype = "ebuild"
443         if len(argv) > 2:
444                 pkgtype = argv[1]
445                 atom = argv[2]
446         else:
447                 atom = argv[1]
448
449         type_map = {
450                 "ebuild":"porttree",
451                 "binary":"bintree",
452                 "installed":"vartree"}
453
454         if pkgtype not in type_map:
455                 writemsg("Unrecognized package type: '%s'\n" % pkgtype,
456                         noiselevel=-1)
457                 return 2
458
459         eroot = argv[0]
460         db = portage.db[eroot][type_map[pkgtype]].dbapi
461
462         try:
463                 atom = portage.dep_expand(atom, mydb=db, settings=portage.settings)
464         except portage.exception.InvalidAtom:
465                 writemsg("ERROR: Invalid atom: '%s'\n" % atom,
466                         noiselevel=-1)
467                 return 2
468
469         root_config = RootConfig(portage.settings,
470                 portage.db[eroot], None)
471
472         if hasattr(db, "xmatch"):
473                 cpv_list = db.xmatch("match-all-cpv-only", atom)
474         else:
475                 cpv_list = db.match(atom)
476
477         if cpv_list:
478                 # reversed, for descending order
479                 cpv_list.reverse()
480                 # verify match, since the atom may match the package
481                 # for a given cpv from one repo but not another, and
482                 # we can use match-all-cpv-only to avoid redundant
483                 # metadata access.
484                 atom_set = InternalPackageSet(initial_atoms=(atom,))
485
486                 if atom.repo is None and hasattr(db, "getRepositories"):
487                         repo_list = db.getRepositories()
488                 else:
489                         repo_list = [atom.repo]
490
491                 for cpv in cpv_list:
492                         for repo in repo_list:
493                                 try:
494                                         metadata = dict(zip(Package.metadata_keys,
495                                                 db.aux_get(cpv, Package.metadata_keys, myrepo=repo)))
496                                 except KeyError:
497                                         continue
498                                 pkg = Package(built=(pkgtype != "ebuild"), cpv=cpv,
499                                         installed=(pkgtype=="installed"), metadata=metadata,
500                                         root_config=root_config, type_name=pkgtype)
501                                 if not atom_set.findAtomForPackage(pkg):
502                                         continue
503
504                                 if pkg.visible:
505                                         writemsg_stdout("%s\n" % (pkg.cpv,), noiselevel=-1)
506                                         return os.EX_OK
507
508         # No package found, write out an empty line.
509         writemsg_stdout("\n", noiselevel=-1)
510
511         return 1
512
513
514 @uses_eroot
515 def mass_best_visible(argv):
516         """<eroot> [<type>] [<category/package>]+
517         Returns category/package-version (without .ebuild).
518         The pkgtype argument defaults to "ebuild" if unspecified,
519         otherwise it must be one of ebuild, binary, or installed.
520         """
521         type_map = {
522                 "ebuild":"porttree",
523                 "binary":"bintree",
524                 "installed":"vartree"}
525
526         if (len(argv) < 2):
527                 print("ERROR: insufficient parameters!")
528                 return 2
529         try:
530                 root = argv.pop(0)
531                 pkgtype = "ebuild"
532                 if argv[0] in type_map:
533                         pkgtype = argv.pop(0)
534                 for pack in argv:
535                         writemsg_stdout("%s:" % pack, noiselevel=-1)
536                         best_visible([root, pkgtype, pack])
537         except KeyError:
538                 return 1
539
540
541 @uses_eroot
542 def all_best_visible(argv):
543         """<eroot>
544         Returns all best_visible packages (without .ebuild).
545         """
546         if len(argv) < 1:
547                 sys.stderr.write("ERROR: insufficient parameters!\n")
548                 sys.stderr.flush()
549                 return 2
550
551         #print portage.db[argv[0]]["porttree"].dbapi.cp_all()
552         for pkg in portage.db[argv[0]]["porttree"].dbapi.cp_all():
553                 mybest=portage.best(portage.db[argv[0]]["porttree"].dbapi.match(pkg))
554                 if mybest:
555                         print(mybest)
556
557
558 @uses_eroot
559 def match(argv):
560         """<eroot> <atom>
561         Returns a \\n separated list of category/package-version.
562         When given an empty string, all installed packages will
563         be listed.
564         """
565         if len(argv) != 2:
566                 print("ERROR: expected 2 parameters, got %d!" % len(argv))
567                 return 2
568         root, atom = argv
569         if not atom:
570                 atom = "*/*"
571
572         vardb = portage.db[root]["vartree"].dbapi
573         try:
574                 atom = portage.dep.Atom(atom, allow_wildcard=True, allow_repo=True)
575         except portage.exception.InvalidAtom:
576                 # maybe it's valid but missing category
577                 atom = portage.dep_expand(atom, mydb=vardb, settings=vardb.settings)
578
579         if atom.extended_syntax:
580                 if atom == "*/*":
581                         results = vardb.cpv_all()
582                 else:
583                         results = []
584                         require_metadata = atom.slot or atom.repo
585                         for cpv in vardb.cpv_all():
586
587                                 if not portage.match_from_list(atom, [cpv]):
588                                         continue
589
590                                 if require_metadata:
591                                         try:
592                                                 cpv = vardb._pkg_str(cpv, atom.repo)
593                                         except (KeyError, portage.exception.InvalidData):
594                                                 continue
595                                         if not portage.match_from_list(atom, [cpv]):
596                                                 continue
597
598                                 results.append(cpv)
599
600                 results.sort()
601         else:
602                 results = vardb.match(atom)
603         for cpv in results:
604                 print(cpv)
605
606
607 @uses_eroot
608 def expand_virtual(argv):
609         """<eroot> <atom>
610         Returns a \\n separated list of atoms expanded from a
611         given virtual atom (GLEP 37 virtuals only),
612         excluding blocker atoms. Satisfied
613         virtual atoms are not included in the output, since
614         they are expanded to real atoms which are displayed.
615         Unsatisfied virtual atoms are displayed without
616         any expansion. The "match" command can be used to
617         resolve the returned atoms to specific installed
618         packages.
619         """
620         if len(argv) != 2:
621                 writemsg("ERROR: expected 2 parameters, got %d!\n" % len(argv),
622                         noiselevel=-1)
623                 return 2
624
625         root, atom = argv
626
627         try:
628                 results = list(expand_new_virt(
629                         portage.db[root]["vartree"].dbapi, atom))
630         except portage.exception.InvalidAtom:
631                 writemsg("ERROR: Invalid atom: '%s'\n" % atom,
632                         noiselevel=-1)
633                 return 2
634
635         results.sort()
636         for x in results:
637                 if not x.blocker:
638                         writemsg_stdout("%s\n" % (x,))
639
640         return os.EX_OK
641
642
643 def vdb_path(argv):
644         """
645         Returns the path used for the var(installed) package database for the
646         set environment/configuration options.
647         """
648         out = sys.stdout
649         out.write(os.path.join(portage.settings["EROOT"], portage.VDB_PATH) + "\n")
650         out.flush()
651         return os.EX_OK
652
653 def gentoo_mirrors(argv):
654         """
655         Returns the mirrors set to use in the portage configuration.
656         """
657         print(portage.settings["GENTOO_MIRRORS"])
658
659
660 @uses_eroot
661 def repositories_configuration(argv):
662         """<eroot>
663         Returns the configuration of repositories.
664         """
665         if len(argv) < 1:
666                 print("ERROR: insufficient parameters!", file=sys.stderr)
667                 return 3
668         sys.stdout.write(portage.db[argv[0]]["vartree"].settings.repositories.config_string())
669         sys.stdout.flush()
670
671 @uses_eroot
672 def repos_config(argv):
673         """
674         <eroot>
675         This is an alias for the repositories_configuration command.
676         """
677         return repositories_configuration(argv)
678
679 def portdir(argv):
680         """
681         Returns the PORTDIR path.
682         """
683         print(portage.settings["PORTDIR"])
684
685
686 def config_protect(argv):
687         """
688         Returns the CONFIG_PROTECT paths.
689         """
690         print(portage.settings["CONFIG_PROTECT"])
691
692
693 def config_protect_mask(argv):
694         """
695         Returns the CONFIG_PROTECT_MASK paths.
696         """
697         print(portage.settings["CONFIG_PROTECT_MASK"])
698
699
700 def portdir_overlay(argv):
701         """
702         Returns the PORTDIR_OVERLAY path.
703         """
704         print(portage.settings["PORTDIR_OVERLAY"])
705
706
707 def pkgdir(argv):
708         """
709         Returns the PKGDIR path.
710         """
711         print(portage.settings["PKGDIR"])
712
713
714 def distdir(argv):
715         """
716         Returns the DISTDIR path.
717         """
718         print(portage.settings["DISTDIR"])
719
720
721 def colormap(argv):
722         """
723         Display the color.map as environment variables.
724         """
725         print(portage.output.colormap())
726
727
728 def envvar(argv):
729         """<variable>+
730         Returns a specific environment variable as exists prior to ebuild.sh.
731         Similar to: emerge --verbose --info | egrep '^<variable>='
732         """
733         verbose = "-v" in argv
734         if verbose:
735                 argv.pop(argv.index("-v"))
736
737         if len(argv) == 0:
738                 print("ERROR: insufficient parameters!")
739                 return 2
740
741         for arg in argv:
742                 if verbose:
743                         print(arg +"='"+ portage.settings[arg] +"'")
744                 else:
745                         print(portage.settings[arg])
746
747
748 @uses_eroot
749 def get_repos(argv):
750         """<eroot>
751         Returns all repos with names (repo_name file) argv[0] = $EROOT
752         """
753         if len(argv) < 1:
754                 print("ERROR: insufficient parameters!")
755                 return 2
756         print(" ".join(reversed(portage.db[argv[0]]["vartree"].settings.repositories.prepos_order)))
757
758
759 @uses_eroot
760 def master_repositories(argv):
761         """<eroot> <repo_id>+
762         Returns space-separated list of master repositories for specified repository.
763         """
764         if len(argv) < 2:
765                 print("ERROR: insufficient parameters!", file=sys.stderr)
766                 return 3
767         for arg in argv[1:]:
768                 if portage.dep._repo_name_re.match(arg) is None:
769                         print("ERROR: invalid repository: %s" % arg, file=sys.stderr)
770                         return 2
771                 try:
772                         repo = portage.db[argv[0]]["vartree"].settings.repositories[arg]
773                 except KeyError:
774                         print("")
775                         return 1
776                 else:
777                         print(" ".join(x.name for x in repo.masters))
778
779 @uses_eroot
780 def master_repos(argv):
781         """<eroot> <repo_id>+
782         This is an alias for the master_repositories command.
783         """
784         return master_repositories(argv)
785
786 @uses_eroot
787 def get_repo_path(argv):
788         """<eroot> <repo_id>+
789         Returns the path to the repo named argv[1], argv[0] = $EROOT
790         """
791         if len(argv) < 2:
792                 print("ERROR: insufficient parameters!", file=sys.stderr)
793                 return 3
794         for arg in argv[1:]:
795                 if portage.dep._repo_name_re.match(arg) is None:
796                         print("ERROR: invalid repository: %s" % arg, file=sys.stderr)
797                         return 2
798                 path = portage.db[argv[0]]["vartree"].settings.repositories.treemap.get(arg)
799                 if path is None:
800                         print("")
801                         return 1
802                 print(path)
803
804
805 @uses_eroot
806 def available_eclasses(argv):
807         """<eroot> <repo_id>+
808         Returns space-separated list of available eclasses for specified repository.
809         """
810         if len(argv) < 2:
811                 print("ERROR: insufficient parameters!", file=sys.stderr)
812                 return 3
813         for arg in argv[1:]:
814                 if portage.dep._repo_name_re.match(arg) is None:
815                         print("ERROR: invalid repository: %s" % arg, file=sys.stderr)
816                         return 2
817                 try:
818                         repo = portage.db[argv[0]]["vartree"].settings.repositories[arg]
819                 except KeyError:
820                         print("")
821                         return 1
822                 else:
823                         print(" ".join(sorted(repo.eclass_db.eclasses)))
824
825
826 @uses_eroot
827 def eclass_path(argv):
828         """<eroot> <repo_id> <eclass>+
829         Returns the path to specified eclass for specified repository.
830         """
831         if len(argv) < 3:
832                 print("ERROR: insufficient parameters!", file=sys.stderr)
833                 return 3
834         if portage.dep._repo_name_re.match(argv[1]) is None:
835                 print("ERROR: invalid repository: %s" % argv[1], file=sys.stderr)
836                 return 2
837         try:
838                 repo = portage.db[argv[0]]["vartree"].settings.repositories[argv[1]]
839         except KeyError:
840                 print("")
841                 return 1
842         else:
843                 retval = 0
844                 for arg in argv[2:]:
845                         try:
846                                 eclass = repo.eclass_db.eclasses[arg]
847                         except KeyError:
848                                 print("")
849                                 retval = 1
850                         else:
851                                 print(eclass.location)
852                 return retval
853
854
855 @uses_eroot
856 def license_path(argv):
857         """<eroot> <repo_id> <license>+
858         Returns the path to specified license for specified repository.
859         """
860         if len(argv) < 3:
861                 print("ERROR: insufficient parameters!", file=sys.stderr)
862                 return 3
863         if portage.dep._repo_name_re.match(argv[1]) is None:
864                 print("ERROR: invalid repository: %s" % argv[1], file=sys.stderr)
865                 return 2
866         try:
867                 repo = portage.db[argv[0]]["vartree"].settings.repositories[argv[1]]
868         except KeyError:
869                 print("")
870                 return 1
871         else:
872                 retval = 0
873                 for arg in argv[2:]:
874                         eclass_path = ""
875                         paths = reversed([os.path.join(x.location, 'licenses', arg) for x in list(repo.masters) + [repo]])
876                         for path in paths:
877                                 if os.path.exists(path):
878                                         eclass_path = path
879                                         break
880                         if eclass_path == "":
881                                 retval = 1
882                         print(eclass_path)
883                 return retval
884
885
886 @uses_eroot
887 def list_preserved_libs(argv):
888         """<eroot>
889         Print a list of libraries preserved during a package update in the form
890         package: path. Returns 1 if no preserved libraries could be found,
891         0 otherwise.
892         """
893
894         if len(argv) != 1:
895                 print("ERROR: wrong number of arguments")
896                 return 2
897         mylibs = portage.db[argv[0]]["vartree"].dbapi._plib_registry.getPreservedLibs()
898         rValue = 1
899         msg = []
900         for cpv in sorted(mylibs):
901                 msg.append(cpv)
902                 for path in mylibs[cpv]:
903                         msg.append(' ' + path)
904                         rValue = 0
905                 msg.append('\n')
906         writemsg_stdout(''.join(msg), noiselevel=-1)
907         return rValue
908
909
910 class MaintainerEmailMatcher(object):
911         def __init__(self, maintainer_emails):
912                 self._re = re.compile("^(%s)$" % "|".join(maintainer_emails))
913
914         def __call__(self, metadata_xml):
915                 match = False
916                 matcher = self._re.match
917                 for x in metadata_xml.maintainers():
918                         if x.email is not None and matcher(x.email) is not None:
919                                 match = True
920                                 break
921                 return match
922
923 class HerdMatcher(object):
924         def __init__(self, herds):
925                 self._herds = frozenset(herds)
926
927         def __call__(self, metadata_xml):
928                 herds = self._herds
929                 return any(x in herds for x in metadata_xml.herds())
930
931
932 def pquery(parser, pquery_option_groups, opts, args):
933         """[options] [atom]+
934         Emulates a subset of Pkgcore's pquery tool.
935         """
936
937         portdb = portage.db[portage.root]['porttree'].dbapi
938         root_config = RootConfig(portdb.settings,
939                 portage.db[portage.root], None)
940
941         def _pkg(cpv, repo_name):
942                 try:
943                         metadata = dict(zip(
944                                 Package.metadata_keys,
945                                 portdb.aux_get(cpv,
946                                 Package.metadata_keys,
947                                 myrepo=repo_name)))
948                 except KeyError:
949                         raise portage.exception.PackageNotFound(cpv)
950                 return Package(built=False, cpv=cpv,
951                         installed=False, metadata=metadata,
952                         root_config=root_config,
953                         type_name="ebuild")
954
955         need_metadata = False
956         atoms = []
957         for arg in args:
958                 if "/" not in arg.split(":")[0]:
959                         atom = insert_category_into_atom(arg, '*')
960                         if atom is None:
961                                 writemsg("ERROR: Invalid atom: '%s'\n" % arg,
962                                         noiselevel=-1)
963                                 return 2
964                 else:
965                         atom = arg
966
967                 try:
968                         atom = portage.dep.Atom(atom, allow_wildcard=True, allow_repo=True)
969                 except portage.exception.InvalidAtom:
970                         writemsg("ERROR: Invalid atom: '%s'\n" % arg,
971                                 noiselevel=-1)
972                         return 2
973
974                 if atom.slot is not None:
975                         need_metadata = True
976
977                 atoms.append(atom)
978
979         if "*/*" in atoms:
980                 del atoms[:]
981                 need_metadata = False
982
983         if not opts.no_filters:
984                 need_metadata = True
985
986         xml_matchers = []
987         if opts.maintainer_email:
988                 maintainer_emails = []
989                 for x in opts.maintainer_email:
990                         maintainer_emails.extend(x.split(","))
991                 xml_matchers.append(MaintainerEmailMatcher(maintainer_emails))
992         if opts.herd is not None:
993                 herds  = []
994                 for x in opts.herd:
995                         herds.extend(x.split(","))
996                 xml_matchers.append(HerdMatcher(herds))
997
998         repos = []
999         if opts.all_repos:
1000                 repos.extend(portdb.repositories.get_repo_for_location(location)
1001                         for location in portdb.porttrees)
1002         elif opts.repo is not None:
1003                 repos.append(portdb.repositories[opts.repo])
1004         else:
1005                 repos.append(portdb.repositories.mainRepo())
1006
1007         if not atoms:
1008                 names = None
1009                 categories = list(portdb.categories)
1010         else:
1011                 category_wildcard = False
1012                 name_wildcard = False
1013                 categories = []
1014                 names = []
1015                 for atom in atoms:
1016                         category, name = portage.catsplit(atom.cp)
1017                         categories.append(category)
1018                         names.append(name)
1019                         if "*" in category:
1020                                 category_wildcard = True
1021                         if "*" in name:
1022                                 name_wildcard = True
1023
1024                 if category_wildcard:
1025                         categories = list(portdb.categories)
1026                 else:
1027                         categories = list(set(categories))
1028
1029                 if name_wildcard:
1030                         names = None
1031                 else:
1032                         names = sorted(set(names))
1033
1034         no_version = opts.no_version
1035         categories.sort()
1036
1037         for category in categories:
1038                 if names is None:
1039                         cp_list = portdb.cp_all(categories=(category,))
1040                 else:
1041                         cp_list = [category + "/" + name for name in names]
1042                 for cp in cp_list:
1043                         matches = []
1044                         for repo in repos:
1045                                 match = True
1046                                 if xml_matchers:
1047                                         metadata_xml_path = os.path.join(
1048                                                 repo.location, cp, 'metadata.xml')
1049                                         try:
1050                                                 metadata_xml = MetaDataXML(metadata_xml_path, None)
1051                                         except (EnvironmentError, SyntaxError):
1052                                                 match = False
1053                                         else:
1054                                                 for matcher in xml_matchers:
1055                                                         if not matcher(metadata_xml):
1056                                                                 match = False
1057                                                                 break
1058                                 if not match:
1059                                         continue
1060                                 cpv_list = portdb.cp_list(cp, mytree=[repo.location])
1061                                 if atoms:
1062                                         for cpv in cpv_list:
1063                                                 pkg = None
1064                                                 for atom in atoms:
1065                                                         if atom.repo is not None and \
1066                                                                 atom.repo != repo.name:
1067                                                                 continue
1068                                                         if not portage.match_from_list(atom, [cpv]):
1069                                                                 continue
1070                                                         if need_metadata:
1071                                                                 if pkg is None:
1072                                                                         try:
1073                                                                                 pkg = _pkg(cpv, repo.name)
1074                                                                         except portage.exception.PackageNotFound:
1075                                                                                 continue
1076
1077                                                                 if not (opts.no_filters or pkg.visible):
1078                                                                         continue
1079                                                                 if not portage.match_from_list(atom, [pkg]):
1080                                                                         continue
1081                                                         matches.append(cpv)
1082                                                         break
1083                                                 if no_version and matches:
1084                                                         break
1085                                 elif opts.no_filters:
1086                                         matches.extend(cpv_list)
1087                                 else:
1088                                         for cpv in cpv_list:
1089                                                 try:
1090                                                         pkg = _pkg(cpv, repo.name)
1091                                                 except portage.exception.PackageNotFound:
1092                                                         continue
1093                                                 else:
1094                                                         if pkg.visible:
1095                                                                 matches.append(cpv)
1096                                                                 if no_version:
1097                                                                         break
1098
1099                                 if no_version and matches:
1100                                         break
1101
1102                         if not matches:
1103                                 continue
1104
1105                         if no_version:
1106                                 writemsg_stdout("%s\n" % (cp,), noiselevel=-1)
1107                         else:
1108                                 matches = list(set(matches))
1109                                 portdb._cpv_sort_ascending(matches)
1110                                 for cpv in matches:
1111                                         writemsg_stdout("%s\n" % (cpv,), noiselevel=-1)
1112
1113         return os.EX_OK
1114
1115
1116 #-----------------------------------------------------------------------------
1117 #
1118 # DO NOT CHANGE CODE BEYOND THIS POINT - IT'S NOT NEEDED!
1119 #
1120
1121 non_commands = frozenset(['elog', 'eval_atom_use', 'exithandler', 'main', 'usage', 'uses_eroot'])
1122 commands = sorted(k for k, v in globals().items() \
1123         if k not in non_commands and isinstance(v, types.FunctionType) and v.__module__ == "__main__")
1124
1125 def usage(argv, parser=None, pquery_option_groups=None):
1126         print(">>> Portage information query tool")
1127         print(">>> %s" % portage.VERSION)
1128         print(">>> Usage: portageq <command> [<option> ...]")
1129         print("")
1130         print("Available commands:")
1131
1132         #
1133         # Show our commands -- we do this by scanning the functions in this
1134         # file, and formatting each functions documentation.
1135         #
1136         help_mode = '--help' in argv
1137         for name in commands:
1138                 # Drop non-functions
1139                 obj = globals()[name]
1140
1141                 doc = obj.__doc__
1142                 if (doc == None):
1143                         print("   " + name)
1144                         print("      MISSING DOCUMENTATION!")
1145                         print("")
1146                         continue
1147
1148                 lines = doc.lstrip("\n").split("\n")
1149                 print("   " + name + " " + lines[0].strip())
1150                 if len(argv) > 1:
1151                         if (not help_mode):
1152                                 lines = lines[:-1]
1153                         for line in lines[1:]:
1154                                 print("      " + line.strip())
1155
1156         if pquery_option_groups is not None:
1157                 parser.formatter.store_option_strings(parser)
1158                 print()
1159                 print('Pkgcore pquery compatible options:')
1160                 print()
1161                 for optgroup in pquery_option_groups:
1162                         print(optgroup.format_help(parser.formatter))
1163
1164         if len(argv) == 1:
1165                 print("\nRun portageq with --help for info")
1166
1167 atom_validate_strict = "EBUILD_PHASE" in os.environ
1168 eapi = None
1169 if atom_validate_strict:
1170         eapi = os.environ.get('EAPI')
1171
1172         def elog(elog_funcname, lines):
1173                 cmd = "source '%s/isolated-functions.sh' ; " % \
1174                         os.environ["PORTAGE_BIN_PATH"]
1175                 for line in lines:
1176                         cmd += "%s %s ; " % (elog_funcname, portage._shell_quote(line))
1177                 subprocess.call([portage.const.BASH_BINARY, "-c", cmd])
1178
1179 else:
1180         def elog(elog_funcname, lines):
1181                 pass
1182
1183 def main(argv):
1184
1185         argv = portage._decode_argv(argv)
1186
1187         nocolor = os.environ.get('NOCOLOR')
1188         if nocolor in ('yes', 'true'):
1189                 portage.output.nocolor()
1190
1191         parser = optparse.OptionParser(add_help_option=False)
1192
1193         # used by envvar
1194         parser.add_option("-v", dest="verbose", action="store_true")
1195
1196         actions = optparse.OptionGroup(parser, 'Actions')
1197         actions.add_option("-h", "--help", action="store_true")
1198         actions.add_option("--version", action="store_true")
1199         parser.add_option_group(actions)
1200
1201         pquery_option_groups = []
1202
1203         repo_optgroup = optparse.OptionGroup(parser,
1204                 'Repository matching options')
1205         repo_optgroup.add_option("--no-filters", action="store_true",
1206                 help="no visibility filters (ACCEPT_KEYWORDS, package masking, etc)")
1207         repo_optgroup.add_option("--repo", action="store",
1208                 help="repo to use (default is PORTDIR if omitted)")
1209         repo_optgroup.add_option("--all-repos", action="store_true",
1210                 help="search all repos")
1211         parser.add_option_group(repo_optgroup)
1212         pquery_option_groups.append(repo_optgroup)
1213
1214         matching_optgroup = optparse.OptionGroup(parser,
1215                 'Package matching options')
1216         matching_optgroup.add_option("--herd", action="append",
1217                 help="exact match on a herd")
1218         matching_optgroup.add_option("--maintainer-email", action="append",
1219                 help="comma-separated list of maintainer email regexes to search for")
1220         parser.add_option_group(matching_optgroup)
1221         pquery_option_groups.append(matching_optgroup)
1222
1223         formatting_optgroup = optparse.OptionGroup(parser,
1224                 'Output formatting')
1225         formatting_optgroup.add_option("-n", "--no-version", action="store_true",
1226                 help="collapse multiple matching versions together")
1227         parser.add_option_group(formatting_optgroup)
1228         pquery_option_groups.append(formatting_optgroup)
1229
1230         opts, args = parser.parse_args(argv[1:])
1231
1232         if opts.help:
1233                 usage(argv, parser=parser, pquery_option_groups=pquery_option_groups)
1234                 return os.EX_OK
1235         elif opts.version:
1236                 print("Portage", portage.VERSION)
1237                 return os.EX_OK
1238
1239         cmd = None
1240         if args and args[0] in commands:
1241                 cmd = args[0]
1242
1243         if cmd == 'pquery':
1244                 cmd = None
1245                 args = args[1:]
1246
1247         if cmd is None:
1248                 return pquery(parser, pquery_option_groups, opts, args)
1249
1250         if opts.verbose:
1251                 # used by envvar
1252                 args.append("-v")
1253
1254         argv = argv[:1] + args
1255
1256         if len(argv) < 2:
1257                 usage(argv)
1258                 sys.exit(os.EX_USAGE)
1259
1260         function = globals()[cmd]
1261         uses_eroot = getattr(function, "uses_eroot", False) and len(argv) > 2
1262         if uses_eroot:
1263                 if not os.path.isdir(argv[2]):
1264                         sys.stderr.write("Not a directory: '%s'\n" % argv[2])
1265                         sys.stderr.write("Run portageq with --help for info\n")
1266                         sys.stderr.flush()
1267                         sys.exit(os.EX_USAGE)
1268                 eprefix = portage.settings["EPREFIX"]
1269                 eroot = portage.util.normalize_path(argv[2])
1270
1271                 if eprefix:
1272                         if not eroot.endswith(eprefix):
1273                                 sys.stderr.write("ERROR: This version of portageq"
1274                                                  " only supports <eroot>s ending in"
1275                                                  " '%s'. The provided <eroot>, '%s',"
1276                                                  " doesn't.\n" % (eprefix, eroot));
1277                                 sys.stderr.flush()
1278                                 sys.exit(os.EX_USAGE)
1279                         root = eroot[:1-len(eprefix)]
1280                 else:
1281                         root = eroot
1282
1283                 os.environ["ROOT"] = root
1284
1285         args = argv[2:]
1286
1287         try:
1288                 if uses_eroot:
1289                         args[0] = portage.settings['EROOT']
1290                 retval = function(args)
1291                 if retval:
1292                         sys.exit(retval)
1293         except portage.exception.PermissionDenied as e:
1294                 sys.stderr.write("Permission denied: '%s'\n" % str(e))
1295                 sys.exit(e.errno)
1296         except portage.exception.ParseError as e:
1297                 sys.stderr.write("%s\n" % str(e))
1298                 sys.exit(1)
1299         except portage.exception.AmbiguousPackageName as e:
1300                 # Multiple matches thrown from cpv_expand
1301                 pkgs = e.args[0]
1302                 # An error has occurred so we writemsg to stderr and exit nonzero.
1303                 portage.writemsg("You specified an unqualified atom that matched multiple packages:\n", noiselevel=-1)
1304                 for pkg in pkgs:
1305                         portage.writemsg("* %s\n" % pkg, noiselevel=-1)
1306                 portage.writemsg("\nPlease use a more specific atom.\n", noiselevel=-1)
1307                 sys.exit(1)
1308
1309 if __name__ == '__main__':
1310         sys.exit(main(sys.argv))
1311
1312 #-----------------------------------------------------------------------------