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