Support both Python 2 and 3 in portage._unicode_encode() and portage._unicode_decode().
[portage.git] / bin / portageq
1 #!/usr/bin/python -O
2 # Copyright 1999-2006 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4 # $Id$
5
6 from __future__ import print_function
7
8 import sys
9 # This block ensures that ^C interrupts are handled quietly.
10 try:
11         import signal
12
13         def exithandler(signum, frame):
14                 signal.signal(signal.SIGINT, signal.SIG_IGN)
15                 signal.signal(signal.SIGTERM, signal.SIG_IGN)
16                 sys.exit(1)
17
18         signal.signal(signal.SIGINT, exithandler)
19         signal.signal(signal.SIGTERM, exithandler)
20
21 except KeyboardInterrupt:
22         sys.exit(1)
23
24 import os
25
26 import types
27
28 # Avoid sandbox violations after python upgrade.
29 pym_path = os.path.join(os.path.dirname(
30         os.path.dirname(os.path.realpath(__file__))), "pym")
31 if os.environ.get("SANDBOX_ON") == "1":
32         sandbox_write = os.environ.get("SANDBOX_WRITE", "").split(":")
33         if pym_path not in sandbox_write:
34                 sandbox_write.append(pym_path)
35                 os.environ["SANDBOX_WRITE"] = \
36                         ":".join(filter(None, sandbox_write))
37         del sandbox_write
38
39 try:
40         import portage
41 except ImportError:
42         sys.path.insert(0, pym_path)
43         import portage
44 del pym_path
45
46 from portage import os
47 from portage.util import writemsg, writemsg_stdout
48
49 #-----------------------------------------------------------------------------
50 #
51 # To add functionality to this tool, add a function below.
52 #
53 # The format for functions is:
54 #
55 #   def function(argv):
56 #       """<list of options for this function>
57 #       <description of the function>
58 #       """
59 #       <code>
60 #
61 # "argv" is an array of the command line parameters provided after the command.
62 #
63 # Make sure you document the function in the right format.  The documentation
64 # is used to display help on the function.
65 #
66 # You do not need to add the function to any lists, this tool is introspective,
67 # and will automaticly add a command by the same name as the function!
68 #
69
70 def has_version(argv):
71         """<root> <category/package>
72         Return code 0 if it's available, 1 otherwise.
73         """
74         if (len(argv) < 2):
75                 print("ERROR: insufficient parameters!")
76                 sys.exit(2)
77         if atom_validate_strict and not portage.isvalidatom(argv[1]):
78                 portage.writemsg("ERROR: Invalid atom: '%s'\n" % argv[1],
79                         noiselevel=-1)
80                 return 2
81         try:
82                 mylist=portage.db[argv[0]]["vartree"].dbapi.match(argv[1])
83                 if mylist:
84                         sys.exit(0)
85                 else:
86                         sys.exit(1)
87         except KeyError:
88                 sys.exit(1)
89 has_version.uses_root = True
90
91
92 def best_version(argv):
93         """<root> <category/package>
94         Returns category/package-version (without .ebuild).
95         """
96         if (len(argv) < 2):
97                 print("ERROR: insufficient parameters!")
98                 sys.exit(2)
99         if atom_validate_strict and not portage.isvalidatom(argv[1]):
100                 portage.writemsg("ERROR: Invalid atom: '%s'\n" % argv[1],
101                         noiselevel=-1)
102                 return 2
103         try:
104                 mylist=portage.db[argv[0]]["vartree"].dbapi.match(argv[1])
105                 print(portage.best(mylist))
106         except KeyError:
107                 sys.exit(1)
108 best_version.uses_root = True
109
110
111 def mass_best_version(argv):
112         """<root> [<category/package>]+
113         Returns category/package-version (without .ebuild).
114         """
115         if (len(argv) < 2):
116                 print("ERROR: insufficient parameters!")
117                 sys.exit(2)
118         try:
119                 for pack in argv[1:]:
120                         mylist=portage.db[argv[0]]["vartree"].dbapi.match(pack)
121                         print(pack+":"+portage.best(mylist))
122         except KeyError:
123                 sys.exit(1)
124 mass_best_version.uses_root = True
125
126 def metadata(argv):
127         """<root> <pkgtype> <category/package> [<key>]+
128         Returns metadata values for the specified package.
129         """
130         if (len(argv) < 4):
131                 print("ERROR: insufficient parameters!", file=sys.stderr)
132                 sys.exit(2)
133
134         root, pkgtype, pkgspec = argv[0:3]
135         metakeys = argv[3:]
136         type_map = {
137                 "ebuild":"porttree",
138                 "binary":"bintree",
139                 "installed":"vartree"}
140         if pkgtype not in type_map:
141                 print("Unrecognized package type: '%s'" % pkgtype, file=sys.stderr)
142                 sys.exit(1)
143         trees = portage.db
144         if os.path.realpath(root) == os.path.realpath(portage.settings["ROOT"]):
145                 root = portage.settings["ROOT"] # contains the normalized $ROOT
146         try:
147                         values = trees[root][type_map[pkgtype]].dbapi.aux_get(
148                                 pkgspec, metakeys)
149                         writemsg_stdout(''.join('%s\n' % x for x in values), noiselevel=-1)
150         except KeyError:
151                 print("Package not found: '%s'" % pkgspec, file=sys.stderr)
152                 sys.exit(1)
153
154 metadata.uses_root = True
155
156 def contents(argv):
157         """<root> <category/package>
158         List the files that are installed for a given package, with
159         one file listed on each line. All file names will begin with
160         <root>.
161         """
162         if len(argv) != 2:
163                 print("ERROR: expected 2 parameters, got %d!" % len(argv))
164                 return 2
165
166         root, cpv = argv
167         vartree = portage.db[root]["vartree"]
168         if not vartree.dbapi.cpv_exists(cpv):
169                 sys.stderr.write("Package not found: '%s'\n" % cpv)
170                 return 1
171         cat, pkg = portage.catsplit(cpv)
172         db = portage.dblink(cat, pkg, root, vartree.settings,
173                 treetype="vartree", vartree=vartree)
174         writemsg_stdout(''.join('%s\n' % x for x in sorted(db.getcontents())),
175                 noiselevel=-1)
176 contents.uses_root = True
177
178 def owners(argv):
179         """<root> [<filename>]+
180         Given a list of files, print the packages that own the files and which
181         files belong to each package. Files owned by a package are listed on
182         the lines below it, indented by a single tab character (\\t). All file
183         paths must either start with <root> or be a basename alone.
184         Returns 1 if no owners could be found, and 0 otherwise.
185         """
186         if len(argv) < 2:
187                 sys.stderr.write("ERROR: insufficient parameters!\n")
188                 sys.stderr.flush()
189                 return 2
190
191         from portage import catsplit, dblink
192         settings = portage.settings
193         root = settings["ROOT"]
194         vardb = portage.db[root]["vartree"].dbapi
195
196         cwd = None
197         try:
198                 cwd = os.getcwd()
199         except OSError:
200                 pass
201
202         files = []
203         for f in argv[1:]:
204                 f = portage.normalize_path(f)
205                 is_basename = os.sep not in f
206                 if not is_basename and f[:1] != os.sep:
207                         if cwd is None:
208                                 sys.stderr.write("ERROR: cwd does not exist!\n")
209                                 sys.stderr.flush()
210                                 return 2
211                         f = os.path.join(cwd, f)
212                         f = portage.normalize_path(f)
213                 if not is_basename and not f.startswith(root):
214                         sys.stderr.write("ERROR: file paths must begin with <root>!\n")
215                         sys.stderr.flush()
216                         return 2
217                 if is_basename:
218                         files.append(f)
219                 else:
220                         files.append(f[len(root)-1:])
221
222         owners = vardb._owners.get_owners(files)
223
224         msg = []
225         for pkg, owned_files in owners.iteritems():
226                 cpv = pkg.mycpv
227                 msg.append("%s\n" % cpv)
228                 for f in sorted(owned_files):
229                         msg.append("\t%s\n" % \
230                                 os.path.join(root, f.lstrip(os.path.sep)))
231
232         writemsg_stdout(''.join(msg), noiselevel=-1)
233
234         if owners:
235                 return 0
236
237         sys.stderr.write("None of the installed packages claim the file(s).\n")
238         sys.stderr.flush()
239         return 1
240
241 owners.uses_root = True
242
243 def is_protected(argv):
244         """<root> <filename>
245         Given a single filename, return code 0 if it's protected, 1 otherwise.
246         The filename must begin with <root>.
247         """
248         if len(argv) != 2:
249                 sys.stderr.write("ERROR: expected 2 parameters, got %d!\n" % len(argv))
250                 sys.stderr.flush()
251                 return 2
252
253         root, filename = argv
254
255         err = sys.stderr
256         cwd = None
257         try:
258                 cwd = os.getcwd()
259         except OSError:
260                 pass
261
262         f = portage.normalize_path(filename)
263         if not f.startswith(os.path.sep):
264                 if cwd is None:
265                         err.write("ERROR: cwd does not exist!\n")
266                         err.flush()
267                         return 2
268                 f = os.path.join(cwd, f)
269                 f = portage.normalize_path(f)
270
271         if not f.startswith(root):
272                 err.write("ERROR: file paths must begin with <root>!\n")
273                 err.flush()
274                 return 2
275
276         from portage.util import ConfigProtect
277
278         settings = portage.settings
279         protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
280         protect_mask = portage.util.shlex_split(
281                 settings.get("CONFIG_PROTECT_MASK", ""))
282         protect_obj = ConfigProtect(root, protect, protect_mask)
283
284         if protect_obj.isprotected(f):
285                 return 0
286         return 1
287
288 is_protected.uses_root = True
289
290 def filter_protected(argv):
291         """<root>
292         Read filenames from stdin and write them to stdout if they are protected.
293         All filenames are delimited by \\n and must begin with <root>.
294         """
295         if len(argv) != 1:
296                 sys.stderr.write("ERROR: expected 1 parameter, got %d!\n" % len(argv))
297                 sys.stderr.flush()
298                 return 2
299
300         root, = argv
301         out = sys.stdout
302         err = sys.stderr
303         cwd = None
304         try:
305                 cwd = os.getcwd()
306         except OSError:
307                 pass
308
309         from portage.util import ConfigProtect
310
311         settings = portage.settings
312         protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
313         protect_mask = portage.util.shlex_split(
314                 settings.get("CONFIG_PROTECT_MASK", ""))
315         protect_obj = ConfigProtect(root, protect, protect_mask)
316
317         protected = 0
318         errors = 0
319
320         for line in sys.stdin:
321                 filename = line.rstrip("\n")
322                 f = portage.normalize_path(filename)
323                 if not f.startswith(os.path.sep):
324                         if cwd is None:
325                                 err.write("ERROR: cwd does not exist!\n")
326                                 err.flush()
327                                 errors += 1
328                                 continue
329                         f = os.path.join(cwd, f)
330                         f = portage.normalize_path(f)
331
332                 if not f.startswith(root):
333                         err.write("ERROR: file paths must begin with <root>!\n")
334                         err.flush()
335                         errors += 1
336                         continue
337
338                 if protect_obj.isprotected(f):
339                         protected += 1
340                         out.write("%s\n" % filename)
341         out.flush()
342
343         if errors:
344                 return 2
345
346         return 0
347
348 filter_protected.uses_root = True
349
350 def best_visible(argv):
351         """<root> [<category/package>]+
352         Returns category/package-version (without .ebuild).
353         """
354         if (len(argv) < 2):
355                 print("ERROR: insufficient parameters!")
356                 sys.exit(2)
357         try:
358                 mylist=portage.db[argv[0]]["porttree"].dbapi.match(argv[1])
359                 visible=portage.best(mylist)
360                 if visible:
361                         print(visible)
362                         sys.exit(0)
363                 else:
364                         sys.exit(1)
365         except KeyError:
366                 sys.exit(1)
367 best_visible.uses_root = True
368
369
370 def mass_best_visible(argv):
371         """<root> [<category/package>]+
372         Returns category/package-version (without .ebuild).
373         """
374         if (len(argv) < 2):
375                 print("ERROR: insufficient parameters!")
376                 sys.exit(2)
377         try:
378                 for pack in argv[1:]:
379                         mylist=portage.db[argv[0]]["porttree"].dbapi.match(pack)
380                         print(pack+":"+portage.best(mylist))
381         except KeyError:
382                 sys.exit(1)
383 mass_best_visible.uses_root = True
384
385
386 def all_best_visible(argv):
387         """<root>
388         Returns all best_visible packages (without .ebuild).
389         """
390         if (len(argv) < 1):
391                 print("ERROR: insufficient parameters!")
392         
393         #print portage.db[argv[0]]["porttree"].dbapi.cp_all()
394         for pkg in portage.db[argv[0]]["porttree"].dbapi.cp_all():
395                 mybest=portage.best(portage.db[argv[0]]["porttree"].dbapi.match(pkg))
396                 if mybest:
397                         print(mybest)
398 all_best_visible.uses_root = True
399
400
401 def match(argv):
402         """<root> <atom>
403         Returns a \\n separated list of category/package-version.
404         When given an empty string, all installed packages will
405         be listed.
406         """
407         if len(argv) != 2:
408                 print("ERROR: expected 2 parameters, got %d!" % len(argv))
409                 sys.exit(2)
410         root, atom = argv
411         if atom:
412                 if atom_validate_strict and not portage.isvalidatom(atom):
413                         portage.writemsg("ERROR: Invalid atom: '%s'\n" % atom,
414                                 noiselevel=-1)
415                         return 2
416                 results = portage.db[root]["vartree"].dbapi.match(atom)
417         else:
418                 results = portage.db[root]["vartree"].dbapi.cpv_all()
419                 results.sort()
420         for cpv in results:
421                 print(cpv)
422 match.uses_root = True
423
424
425 def vdb_path(argv):
426         """
427         Returns the path used for the var(installed) package database for the
428         set environment/configuration options.
429         """
430         out = sys.stdout
431         out.write(os.path.join(portage.settings["ROOT"], portage.VDB_PATH) + "\n")
432         out.flush()
433         return os.EX_OK
434
435 def gentoo_mirrors(argv):
436         """
437         Returns the mirrors set to use in the portage configuration.
438         """
439         print(portage.settings["GENTOO_MIRRORS"])
440
441
442 def portdir(argv):
443         """
444         Returns the PORTDIR path.
445         """
446         print(portage.settings["PORTDIR"])
447
448
449 def config_protect(argv):
450         """
451         Returns the CONFIG_PROTECT paths.
452         """
453         print(portage.settings["CONFIG_PROTECT"])
454
455
456 def config_protect_mask(argv):
457         """
458         Returns the CONFIG_PROTECT_MASK paths.
459         """
460         print(portage.settings["CONFIG_PROTECT_MASK"])
461
462
463 def portdir_overlay(argv):
464         """
465         Returns the PORTDIR_OVERLAY path.
466         """
467         print(portage.settings["PORTDIR_OVERLAY"])
468
469
470 def pkgdir(argv):
471         """
472         Returns the PKGDIR path.
473         """
474         print(portage.settings["PKGDIR"])
475
476
477 def distdir(argv):
478         """
479         Returns the DISTDIR path.
480         """
481         print(portage.settings["DISTDIR"])
482
483
484 def envvar(argv):
485         """<variable>+
486         Returns a specific environment variable as exists prior to ebuild.sh.
487         Similar to: emerge --verbose --info | egrep '^<variable>='
488         """
489         verbose = "-v" in argv
490         if verbose:
491                 argv.pop(argv.index("-v"))
492
493         if len(argv) == 0:
494                 print("ERROR: insufficient parameters!")
495                 sys.exit(2)
496
497         for arg in argv:
498                 if verbose:
499                         print(arg +"='"+ portage.settings[arg] +"'")
500                 else:
501                         print(portage.settings[arg])
502
503 def get_repos(argv):
504         """<root>
505         Returns all repos with names (repo_name file) argv[0] = $ROOT
506         """
507         if len(argv) < 1:
508                 print("ERROR: insufficient parameters!")
509                 sys.exit(2)
510         print(" ".join(portage.db[argv[0]]["porttree"].dbapi.getRepositories()))
511
512 def get_repo_path(argv):
513         """<root> <repo_id>+
514         Returns the path to the repo named argv[1], argv[0] = $ROOT
515         """
516         if len(argv) < 2:
517                 print("ERROR: insufficient parameters!")
518                 sys.exit(2)
519         for arg in argv[1:]:
520                 print(portage.db[argv[0]]["porttree"].dbapi.getRepositoryPath(arg))
521
522 def list_preserved_libs(argv):
523         """<root>
524         Print a list of libraries preserved during a package update in the form
525         package: path. Returns 0 if no preserved libraries could be found, 
526         1 otherwise.
527         """
528
529         if len(argv) != 1:
530                 print("ERROR: wrong number of arguments")
531                 sys.exit(2)
532         mylibs = portage.db[argv[0]]["vartree"].dbapi.plib_registry.getPreservedLibs()
533         rValue = 0
534         msg = []
535         for cpv in sorted(mylibs):
536                 msg.append(cpv)
537                 for path in mylibs[cpv]:
538                         msg.append(' ' + path)
539                         rValue = 1
540                 msg.append('\n')
541         writemsg_stdout(''.join(msg), noiselevel=-1)
542         return rValue
543 list_preserved_libs.uses_root = True
544
545 #-----------------------------------------------------------------------------
546 #
547 # DO NOT CHANGE CODE BEYOND THIS POINT - IT'S NOT NEEDED!
548 #
549
550 def usage(argv):
551         print(">>> Portage information query tool")
552         print(">>> $Id$")
553         print(">>> Usage: portageq <command> [<option> ...]")
554         print("")
555         print("Available commands:")
556
557         #
558         # Show our commands -- we do this by scanning the functions in this
559         # file, and formatting each functions documentation.
560         #
561         non_commands = frozenset(['exithandler', 'main',
562                 'usage', 'writemsg', 'writemsg_stdout'])
563         commands = sorted(k for k, v in globals().iteritems() \
564                 if type(v) is types.FunctionType and k not in non_commands)
565
566         for name in commands:
567                 # Drop non-functions
568                 obj = globals()[name]
569
570                 doc = obj.__doc__
571                 if (doc == None):
572                         print("   "+name)
573                         print("      MISSING DOCUMENTATION!")
574                         print("")
575                         continue
576
577                 lines = doc.split("\n")
578                 print("   "+name+" "+lines[0].strip())
579                 if (len(sys.argv) > 1):
580                         if ("--help" not in sys.argv):
581                                 lines = lines[:-1]
582                         for line in lines[1:]:
583                                 print("      "+line.strip())
584         if (len(sys.argv) == 1):
585                 print("\nRun portageq with --help for info")
586
587 atom_validate_strict = "EBUILD_PHASE" in os.environ
588
589 def main():
590         if "-h" in sys.argv or "--help" in sys.argv:
591                 usage(sys.argv)
592                 sys.exit(os.EX_OK)
593         elif len(sys.argv) < 2:
594                 usage(sys.argv)
595                 sys.exit(os.EX_USAGE)
596
597         cmd = sys.argv[1]
598         function = globals().get(cmd)
599         if function is None:
600                 usage(sys.argv)
601                 sys.exit(os.EX_USAGE)
602         function = globals()[cmd]
603         uses_root = getattr(function, "uses_root", False) and len(sys.argv) > 2
604         if uses_root:
605                 if not os.path.isdir(sys.argv[2]):
606                         sys.stderr.write("Not a directory: '%s'\n" % sys.argv[2])
607                         sys.stderr.write("Run portageq with --help for info\n")
608                         sys.stderr.flush()
609                         sys.exit(os.EX_USAGE)
610                 os.environ["ROOT"] = sys.argv[2]
611
612         args = sys.argv[2:]
613         if args and sys.hexversion < 0x3000000 and not isinstance(args[0], unicode):
614                 for i in range(len(args)):
615                         args[i] = portage._unicode_decode(args[i])
616
617         try:
618                 if uses_root:
619                         args[0] = portage.settings["ROOT"]
620                 retval = function(args)
621                 if retval:
622                         sys.exit(retval)
623         except portage.exception.PermissionDenied as e:
624                 sys.stderr.write("Permission denied: '%s'\n" % str(e))
625                 sys.exit(e.errno)
626         except portage.exception.ParseError as e:
627                 sys.stderr.write("%s\n" % str(e))
628                 sys.exit(1)
629         except ValueError as e:
630                 if not e.args or \
631                         not hasattr(e.args[0], "__len__") or \
632                         len(e.args[0]) < 2:
633                         raise
634                 # Multiple matches thrown from cpv_expand
635                 pkgs = e.args[0]
636                 # An error has occurred so we writemsg to stderr and exit nonzero.
637                 portage.writemsg("You specified an unqualified atom that matched multiple packages:\n", noiselevel=-1)
638                 for pkg in pkgs:
639                         portage.writemsg("* %s\n" % pkg, noiselevel=-1)
640                 portage.writemsg("\nPlease use a more specific atom.\n", noiselevel=-1)
641                 sys.exit(1)
642
643 main()
644
645 #-----------------------------------------------------------------------------