72f8666fe6c013667a68a8c96fcdd7b06e4bb1c0
[portpeek.git] / portpeek
1 #!/usr/bin/python
2 #
3 # Copyright 2006 Mike Pagano
4 # Distributed under the terms of the GNU General Public License v2
5 #
6 # $Header$
7 # Author: Mike Pagano <mpagano@gentoo.org>
8 #
9 # Portions written ripped from 
10 # - equery, by Karl Trygve Kalleberg <karltk@gentoo.org>
11 # - gentoolkit.py, by Karl Trygve Kalleberg <karltk@gentoo.org>
12 # - portage.py
13 # - emerge
14 #
15 #
16
17 __author__ = "Michael Pagano"
18 __email__ = "mpagano@gentoo.org"
19 __version__ = "2.0.10"
20 __productname__ = "portpeek"
21 __description__ = "Displays user unmasked ebuilds and installable options from the portage tree"
22
23 import sys, os, portage.output, fileinput
24 #import gentoolkit,gentoolkit.helpers,gentoolkit.package,gentoolkit.versionmatch,gentoolkit.query
25 from gentoolkit.versionmatch import VersionMatch
26 from portage.const import USER_CONFIG_PATH
27 from portage.versions import catpkgsplit,pkgcmp,pkgsplit
28 from portage.exception import InvalidAtom
29 from gentoolkit.cpv import CPV
30 from gentoolkit.query import Query
31
32 porttree = portage.db[portage.root]["porttree"]
33 settings = portage.config(clone=portage.settings)
34
35 show_changes_only_flag = False
36 checking_package_unmask = False
37 checking_package_mask = False
38 print_overlay_flag = False
39 info = 0
40 debug = 1
41 logLevel = info
42 show_removable_only_flag = False
43 stable_list = []
44 stable_listNg = [] # handle package.accept_keywords
45 unmask_list = []
46 tilde = 0
47 processing_package_use = False
48
49 try:
50     PORTAGE_CONFIGROOT
51 except NameError:
52     PORTAGE_CONFIGROOT="/"
53
54 USER_CONFIG_PATH=PORTAGE_CONFIGROOT + USER_CONFIG_PATH
55
56 #parameters
57 options = [
58 "--keyword",
59 "--unmask",
60 "--mask",
61 "--all",
62 "--changes-only",
63 "--version",
64 "--version",
65 "--help",
66 "--removable-only",
67 "--debug",
68 "--fix", 
69 "--tilde-check",
70 "--no-color",
71 "--package-use"
72 ]
73
74 mappings = {
75 "k":"--keyword",
76 "u":"--unmask",
77 "m":"--mask",
78 "a":"--all",
79 "c":"--changes-only",
80 "V":"--version",
81 "v":"--version",
82 "h":"--help",
83 "r":"--removable-only",
84 "d":"--debug",
85 "f":"--fix", 
86 "t":"--tilde-check",
87 "n":"--no-color",
88 "s":"--package-use"
89 }
90
91 cmdline = []
92 overlays = [settings["PORTDIR_OVERLAY"]]
93
94 if len(overlays) > 0:
95     overlay_list = overlays[0].split(" ")
96
97 def print_usage():
98     # Print full usage information for this tool to the console.
99     print ("\nUsage: " + portage.output.turquoise(__productname__) +  portage.output.yellow(" command "))
100     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [ options ]") +  portage.output.yellow(" command "))
101     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-c]") + portage.output.yellow(" [akmu]"))
102     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-r]") + portage.output.yellow(" [akmu]"))
103     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-f]") + portage.output.yellow(" [akmu]"))
104     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-F]") + portage.output.yellow(" [akmu]"))
105     print (portage.output.yellow(" command ") + " can be ")
106     print (portage.output.yellow(" -a, --all") + "       - show all matches")
107     print (portage.output.yellow(" -k, --keyword") + "       - show matches from package.keywords and package.accept_keywords only")
108     print (portage.output.yellow(" -m, --mask") + "      - show matches from package.mask only")
109     print (portage.output.yellow(" -u, --unmask") + "        - show matched from package.unmask only")
110     
111     print (portage.output.yellow(" -f, --fix") + "       - will remove the stabled packages without asking for confirmation")
112     print (portage.output.yellow(" -h, --help") + "      - display this message")
113     print (portage.output.yellow(" -d, --debug") + "         - display more verbose output for debugging")
114     print (portage.output.yellow(" -V, --version") + "       - display version info")
115     print (portage.output.green("options") + " are ")
116     print (portage.output.green(" -c, --changes-only") + \
117         "   - show all matches that have upgrade option, use with " + \
118         "<" + portage.output.yellow(" k ") + "|" + portage.output.yellow(" u ") + \
119         "|" + portage.output.yellow(" m ") + "|" + \
120         portage.output.yellow(" a ") + ">")
121     print (portage.output.green(" -n, --no-color") + \
122         "   - suppress color output")
123     print (portage.output.green(" -r, --removable-only") + \
124         "   - show all matches that can be removed from package files, use with " + \
125         "<" + portage.output.yellow(" k ") + "|" + portage.output.yellow(" u ") + \
126         "|" + portage.output.yellow(" m ") + "|" + \
127         portage.output.yellow(" a ") + ">\n")
128
129 def get_keywords(package, var):
130     mytree = porttree
131     filtered_keywords = ""
132     try:
133         keywords = package.environment("KEYWORDS").split()
134     except KeyError as error:
135         print ("!!! Portpeek caught Exception:", error)
136         print ("!!! This package/version seems to be no longer available, " + \
137         "please check and update/unmerge it") 
138         return "Not Available/Deprecated"
139     
140     #filtered_keywords = filter_keywords(keywords[0])
141     filtered_keywords = filter_keywords(keywords)
142     return filtered_keywords
143
144
145
146 # this is the main function for portpeek
147 # TODO comment this code!
148 def parse_line(line, filename):     
149     global info,debug
150     pkgs = None
151     ebuild_output = ""
152     check_pkg = ""
153     not_installed_pkgs = 0
154     pkg_length = 0
155     atom_check="<>="
156
157     print_output(debug,portage.output.blue("Analyzing line: " + line))
158         # determine if we are also check ~ prefixed code
159     if  (tilde == 1): 
160         atom_check="<>=~"
161
162     diffs_found = False
163     display_done = False
164
165     fields = line.replace("\t", " ").split(" ")
166     if len(fields) > 0:
167         #if the line does not start with an atom such as (= or >), do not validate 
168         #status as this tool is for check specific versions and not entire packages 
169         # a ~cpv should be handled like an =cpv if requested bythe parameter -t
170         check_pkg = fields[0] # this should be one of <>=~
171         if check_pkg[0] not in atom_check:
172             return
173         if (tilde == 1):
174             if check_pkg[0] in "~":
175                 check_tilde_masked_pkg(check_pkg, filename)
176                 return
177
178                 # determine if the package exists
179         try:
180             package_exists = portage.portdb.xmatch("match-all", fields[0])
181         except InvalidAtom:
182             package_exists = False
183
184         if package_exists:
185                         # return a Package List based on the cpv
186             query = Query(fields[0])
187             pkgs = []
188
189             try:
190                 pkgs = query.smart_find(True,True,True,True,False,True)
191             except errors.GentoolkitException as err:
192                 pass
193
194             if (pkgs != None):
195                 pkg_length = len(pkgs)
196                 not_installed_pkgs = 0
197                 display_done = False
198
199                                 # go through each package version for a specific version found above
200                 for current_package in pkgs:
201                     if not current_package.is_installed():
202
203                                                 # we have found a package that is in our file, but not installed
204                         not_installed_pkgs = not_installed_pkgs + 1
205
206                         # check to see if specific version of pkg is not installed 
207                         # and display if true
208                         check_pkg = fields[0]
209                         if check_pkg[0] in atom_check:
210                             check_pkg = check_pkg[1:]
211
212                         if (check_pkg == str(current_package.cpv)):
213                             if (not checking_package_mask):
214                                                                 # package is not instaleld
215                                 print_output(info,portage.output.green("\n" + str(current_package.cpv) + ": ") + portage.output.yellow("Not Installed") , current_package, filename)
216                                 if "package.keywords" in filename:
217                                     stable_list.append(str(current_package.cpv))
218                                 if "package.accept_keywords" in filename:
219                                     stable_listNg.append(str(current_package.cpv))
220                                 unmask_list.append(str(current_package.cpv))
221                             else:
222                                                                 # package is masked, and not installed, this is normal use of package.mask
223                                 print_output(info,portage.output.green("" + str(current_package.cpv) + ": ") + portage.output.yellow("Package Masked"),current_package, filename)
224                             display_done = True
225                         continue
226
227                                         # package is installed
228                                         # retrieve the keywords for a file
229                     keywords = "%s" % (get_keywords(current_package,"KEYWORDS").split())
230                     if (keywords.find("Available/Deprecated") >= 0):
231                         continue
232
233                                         #retrieve the mask status of a specific package
234                     pkgmask = _get_mask_status(current_package, False)
235
236                     #determine if installed package is unmasked, if so, display keywords as green
237                     stable = check_for_stable_release(current_package)
238
239                     # do not display if keywords don't exist
240                     if keywords == "[]":
241                         continue
242
243                     if stable:
244                         ebuild_output = portage.output.green("Installed: ") + \
245                             portage.output.turquoise(str(current_package.cpv)) + \
246                             portage.output.green("  Keywords " + keywords)
247                         if "package.unmask" in filename:
248                             unmask_list.append(str(current_package.cpv))
249                         if "package.keywords" in filename:
250                             stable_list.append(str(current_package.cpv))
251                         if "package.accept_keywords" in filename:
252                             stable_listNg.append(str(current_package.cpv))
253                     else:
254                         if (not show_removable_only_flag):
255                             ebuild_output = portage.output.green("Installed: ") + \
256                                 portage.output.turquoise(str(current_package.cpv)) + \
257                                 portage.output.yellow("  Keywords " + keywords) 
258                         else:
259                             ebuild_output = portage.output.yellow(str(current_package.cpv))
260
261                                         # check package.unmask
262                     if (checking_package_unmask):
263                         if (not is_pkg_package_masked(str(current_package.cpv))):
264
265                                                         # package is in package.unmask unnecessarily
266                             ebuild_output = ebuild_output + ": " + portage.output.yellow("Not package masked")
267                             if "package.unmask" in filename:
268                                 unmask_list.append(str(current_package.cpv))
269                                 print_output (info, "" +  ebuild_output,None, filename)
270                                 continue
271
272                                         # print once
273                     ebuild_search_key_printed = False
274                     if stable:
275                         diffs_found = False
276                         ebuild_search_key_printed = True
277                         print_output(info,"\n" + ebuild_output,current_package, filename)
278                     elif not show_changes_only_flag and not show_removable_only_flag:
279                         diffs_found = False
280                         ebuild_search_key_printed = True
281                         print_output(info,"\n" + ebuild_output,current_package)
282
283                                         # go through all versions of a package
284                     query = Query(current_package.category + "/" + current_package.name)
285
286                     all_pkgs = []
287
288                     try:
289                         all_pkgs = query.smart_find(True,True,True,True,False,True)
290                     except errors.GentoolkitException as err:
291                         print_output(debug,portage.output.blue("Package " + current_package.category + "/" + current_package.name + " not found."))
292
293                     for a_package in all_pkgs:
294                         if not a_package.is_installed():
295                                                         # a_package is not installed
296                             pkgmask = _get_mask_status(a_package, False)
297                                                         # print status line of package we are now checking
298                             print_output(debug,portage.output.blue("Checking package: " + str(a_package.cpv) +".pkgmask is " + str(pkgmask)))
299                                                         # if package versions are different
300                             if (VersionMatch(CPV(current_package.cpv)).match(CPV(a_package.cpv))):
301                                 diffs_found = True
302                                 keylist = a_package.environment("KEYWORDS")
303                                 keywords = "%s" % (filter_keywords(keylist)).split()
304                                 #if a_package is masked
305                                 if pkgmask > 0 and not show_removable_only_flag:
306                                     if show_changes_only_flag and not ebuild_search_key_printed:
307                                         print_output (info, "\n" +  ebuild_output, current_package)
308                                         ebuild_search_key_printed = True
309                                         check_for_stable_release(current_package)
310                                     if (pkgmask >= 3):
311                                         print_output (info,portage.output.red("Available: " + str(a_package.cpv) + " [M] Keywords: " + keywords),a_package)
312                                     else:
313                                         print_output (info,portage.output.brown("Available: " + str(a_package.cpv) + " Keywords: " + keywords),a_package)
314                                 else:
315                                     if show_changes_only_flag and not ebuild_search_key_printed:
316                                         print_output (info,"\n" + ebuild_output,current_package)
317                                         ebuild_search_key_printed = True
318                                         print_output(info,portage.output.green("Available: " + str(a_package.cpv) + " Keywords: " + keywords),a_package)
319                 # display if pkg/cat is not installed (missing version)
320                 if not_installed_pkgs ==  pkg_length:
321                     if not display_done:
322                         if (not checking_package_mask):
323                             print_output(info,portage.output.green("\n" + fields[0] + ": ") + portage.output.yellow("Not Installed"),current_package)
324                             if "package.keywords" in filename:
325                                 stable_list.append(str(current_package.cpv))
326                             if "package.accept_keywords" in filename:
327                                 stable_listNg.append(str(current_package.cpv))
328                             unmask_list.append(str(current_package.cpv))
329                         else:
330                             print_output (info,portage.output.green("\n" + str(current_package.cpv) + ": ") + portage.output.yellow("Package Masked"),current_package)
331         else:
332             diffs_found = True
333             print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
334             if "package.keywords" in filename:
335                 stable_list.append(fields[0])
336             if "package.accept_keywords" in filename:
337                 stable_listNg.append(fields[0])
338             unmask_list.append(fields[0])
339             show_all_versions(fields[0], filename)
340     current_package = ""
341
342     return diffs_found
343
344 # adding support for etc/portage/package.keywords/<whatever>/package.keywords
345 def get_recursive_info(filename):
346
347     # determine if filename is a directory
348     if os.path.isdir(filename):
349         # get listing of directory
350         filenames = os.listdir(filename)
351         for file_name in filenames:
352             get_recursive_info(filename+os.path.sep+file_name)
353     else:   
354         get_info(filename)
355
356 def get_info(filename):
357
358     diffs_found = False
359     no_file = False
360     filedescriptor = None
361
362     try:
363         filedescriptor = open(filename)
364         for line in filedescriptor.readlines():
365             line = line.strip()
366             if len(line) <= 0:
367                 continue
368             elif line.find("#") >= 0:
369                 # found '#' remove comment
370                 if (skipFile(line,filename)):
371                     return
372                 line = line[0:line.find("#")]
373                 line = line.strip()
374                 if len(line) <= 0:
375                     continue
376             if (processing_package_use == False):
377                 diffs_found = parse_line(line, filename)
378             else:
379                 # process package.use
380                 diffs_found = parse_package_use(line,filename)
381     except IOError:
382         print (portage.output.red("Could not find file " + filename))
383         no_file = True
384
385     if not diffs_found and no_file:
386         print (portage.output.brown("No ebuild options found."))
387
388     # close file
389     if (filedescriptor != None):
390         filedescriptor.close()
391
392 # skip the file if portpeek-skip found in a comment
393 # you can put this in the middle of a file and it will
394 #skip all entrie below it, this is useful to speed things
395 #up by not checking ~kde versions, for example
396 def skipFile (line, filename):
397     if ( (line == None) or (filename == None)):
398         return False
399
400     if line.find("portpeek-skip") >= 0:
401         return True
402
403     return False
404
405 # parse the package.use file and look for packages
406 # not installed
407 def parse_package_use(line, filename):
408     global info,debug
409     pkgs = None
410     check_pkg = ""
411     pkg_length = 0
412     atom_check="<>="
413     any_version = False
414     has_atom = True
415
416     diffs_found = False
417     package_installed = False
418     fields = line.replace("\t", " ").split(" ")
419
420     if len(fields) > 0:
421         check_pkg = fields[0] # this could be one of <>=
422         if check_pkg[0] not in atom_check:
423             has_atom = False
424         else:
425             check_pkg = check_pkg[1:]
426
427         # look for any version of check_pkg installed as there is 
428         # no version specified in package.use
429         package_exists = portage.portdb.xmatch("match-all", check_pkg)
430         if package_exists:
431             # get all package versions
432             query = Query(check_pkg)
433             pkgs = []
434             try:
435                 pkgs = query.smart_find(True,True,True,True,False,True)
436             except errors.GentoolkitException as err:
437                 print_output(debug,portage.output.blue("Package " + check_pkg + " not found."))
438
439             if (pkgs != None):
440                 pkg_length = len(pkgs)
441
442                 # go through each package version for a specific version found above
443                 if (has_atom == False):
444                     for current_package in pkgs:
445                         if current_package.is_installed():
446                             package_installed = True
447                 else: 
448                     # go through each package version for a specific version found above
449                     for current_package in pkgs:
450                         if (str(current_package.cpv) == check_pkg):
451                             if not current_package.is_installed():
452                                 print_output(info,portage.output.green("\n" + check_pkg + ": ") + portage.output.yellow("Not Installed"),current_package)
453                                 if "package.keywords" in filename:
454                                     stable_list.append(check_pkg)
455                                 if "package.accept_keywords" in filename:
456                                     stable_listNg.append(check_pkg)
457                                 unmask_list.append(check_pkg)
458                                 return True
459                             else:
460                                 return False
461         else:
462             print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
463             if "package.keywords" in filename:
464                 stable_list.append(check_pkg)
465             if "package.accept_keywords" in filename:
466                 stable_listNg.append(check_pkg)
467             unmask_list.append(check_pkg)
468             return True
469         if (package_installed == False):
470             # package does not exists
471             print_output(info,portage.output.green("\n" + check_pkg + ": ") + portage.output.yellow("Not Installed"),current_package)
472             if "package.keywords" in filename:
473                 stable_list.append(check_pkg)
474             if "package.accept_keywords" in filename:
475                 stable_listNg.append(check_pkg)
476             unmask_list.append(check_pkg)
477             return True
478
479     return False
480
481
482 # parts blatantly stolen from equery
483 # if pure is true, then get "true" mask status that is
484 # not affected by entries in /etc/portage/package.*
485 def _get_mask_status(pkg, pure):
486     pkgmask = 0
487
488     if (pkg == None):
489         return 0
490
491     if pkg.is_masked():
492         pkgmask = pkgmask + 3
493
494     if pure:
495         try:
496             keywords = portage.portdb.aux_get(str(pkg.cpv), ["KEYWORDS"])
497             keywords = keywords[0].split()
498         except KeyError:
499             # cpv does not exist
500             return 0
501     else:
502         keywords = pkg.environment("KEYWORDS").split()
503
504     # first check for stable arch, stop there if it is found
505     if settings["ARCH"] in keywords:
506         return 0
507
508     if "~" + settings["ARCH"] in keywords:
509         pkgmask = pkgmask + 1
510     elif "-*" in keywords or "-" + settings["ARCH"] in keywords:
511         pkgmask = pkgmask + 2
512     
513     return pkgmask
514
515 def is_pkg_package_masked(cpv):
516
517     mysplit = catpkgsplit(cpv)
518     if not mysplit:
519         raise ValueError("invalid CPV: %s" % cpv)
520     if not portage.portdb.cpv_exists(cpv):
521         return False
522
523     mycp = mysplit[0] + "/" + mysplit[1]
524
525     pmaskdict = settings._mask_manager._pmaskdict
526     if mycp in pmaskdict:
527         for package in pmaskdict[mycp]:
528             if cpv in portage.portdb.xmatch("match-all", package):
529                 return True
530                 
531     return False    
532
533 # filter out keywords for archs other than the current one
534 def filter_keywords(keywords):
535     filtered_keywords = ""
536
537     #for key in key_list:
538     for key in keywords:
539         key = key.replace("[", '')
540         key = key.replace("]", "")
541         key = key.replace(",", "")
542         arch=settings["ARCH"]
543
544         # remove '~' from key for comparison
545         key_comparison = key.lstrip('~')
546         if key_comparison == arch:
547             if len(filtered_keywords) != 0:
548                 filtered_keywords = filtered_keywords + " "
549             filtered_keywords = filtered_keywords + key
550         elif "-*" in key:
551             if len(filtered_keywords) != 0:
552                 filtered_keywords = filtered_keywords + " "
553             filtered_keywords = filtered_keywords + key
554
555
556     return filtered_keywords
557
558 # check to see if we have a stable release
559 # in our package.* files that we can remove
560 def check_for_stable_release(pkg):
561     if not is_pkg_package_masked(str(pkg.cpv)):
562         status = _get_mask_status(pkg, True)
563         if status == 0:
564             return True
565     return False
566
567 #print version info
568 def print_version():
569     # Print the version of this tool to the console.
570     print (__productname__ + "(" + __version__ + ") - " + \
571         __description__)
572     print ("Author(s): " + __author__)
573
574 # function to go through a ~cp without a version
575 # and set for removal from file if no masked package version exists
576 def check_tilde_masked_pkg(package_name, filename):
577     ebuild_output=""
578     variable_version = ""
579
580     orig_package_name = package_name
581     package_name = package_name.replace('~','')
582
583     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: orig_package-name is " + orig_package_name))
584     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: package_name is " + package_name))
585
586     query = Query(package_name+"*", True)
587
588     packages = []
589     try:
590         packages = query.smart_find(True,True,True,True,False,True)
591     except errors.GentoolkitException as err:
592         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
593
594     no_versions_installed = True
595     for package in packages:
596         if package.is_installed():
597             no_versions_installed = False
598
599     if (no_versions_installed == True):
600         ebuild_output = portage.output.green("\n" + package_name + ": ") + portage.output.yellow("Not Installed")
601         if "package.unmask" in filename:
602             unmask_list.append(orig_package_name)
603         if "package.keywords" in filename:
604             stable_list.append(orig_package_name)
605         if "package.accept_keywords" in filename:
606             stable_listNg.append(orig_package_name)
607         print (ebuild_output + portage.output.brown(" : " + filename))
608
609         return
610
611     # get all packages matching cat/package
612     if (packages != None):
613         for current_package in packages:
614             print_output(debug,portage.output.blue("check_tilde_maskd_pkg: current_package is " + str(current_package.cpv)))
615             print_output(debug,portage.output.blue("comparing " + package_name + " to " + str(current_package.cpv)))
616             if (pkgcmp(pkgsplit(package_name),pkgsplit(str(current_package.cpv))) <=0 ):
617             #if (pkgcmp(package_name, str(current_package.cpv)) <= 0):
618                 packageObj = gentoolkit.package.Package(str(current_package.cpv))
619                 if (packageObj == None):
620                     # we could not create a package object
621                     return
622                 pkgmask = _get_mask_status(packageObj, True)
623
624                 print_output(debug,portage.output.blue("check_tilde_maskd_pkg: current_package is " + str(current_package.cpv) + " and pkgmask is " + str(pkgmask)))
625
626                 if "package.unmask" in filename:
627                     if (is_pkg_package_masked(str(current_package.cpv))):
628                         # package was found as masked
629                         return
630                     else:
631                         # package is not masked
632                         unmask_list.append(str(current_package.cpv))
633                         ebuild_output = portage.output.yellow(str(current_package.cpv)) + ": " + portage.output.yellow("Not package masked")
634                         print_output(info,ebuild_output, package, filename)
635                         return
636                 else:
637                     if (pkgmask >= 1):
638                         # package was found as masked
639                         return 
640                     else:
641                         # at this point we have no packages >= ~cpv that are masked, present for removal
642                         # package does not have any masked versions
643                         ebuild_output = portage.output.green(package_name + " has no masked versions")
644                         
645                         if "package.unmask" in filename:
646                             unmask_list.append(orig_package_name)
647                         if "package.keywords" in filename:
648                             stable_list.append(orig_package_name)
649                         if "package.accept_keywords" in filename:
650                             stable_listNg.append(orig_package_name)
651                         print_output(info,ebuild_output, package, filename)
652
653 #helper function to print avail pks when version does not exist
654 def show_all_versions(pkg, filename):
655
656     # is package masked
657     is_package_masked = False
658     pkgArr = portage.pkgsplit(pkg)
659
660     if pkgArr is None or len(pkgArr) == 0:
661         return
662
663     # determine if category/package is masked 
664     package = pkgArr[0]
665
666     operator = portage.dep.Atom(pkg).operator
667     if operator is not None:
668         package = package[len(operator):]
669
670     # package is category/package and pkg is category/package-version
671     # is category/package-version we are checking package masked?
672     #if portage.settings.pmaskdict.has_key(package):
673     pmaskdict = settings._mask_manager._pmaskdict
674     if package in pmaskdict:
675         pkg_list = pmaskdict.get(package)
676         # iterate through list array looking for pkg
677         for pkg_check in pkg_list:
678             operator = portage.get_operator(pkg_check)
679             if operator is None:
680                 if pkg_check == package:
681                     is_package_masked = True
682
683     query = Query(package)
684
685     all_pkgs = []
686
687     try:
688         all_pkgs = query.smart_find(True,True,True,True,False,True)
689     except errors.GentoolkitException as err:
690         print_output(debug,portage.output.blue("Package " + package + " not found."))
691
692     for current_package in all_pkgs:
693         keywords = "%s" % (current_package.environment("KEYWORDS").split())
694         keywords = filter_keywords(keywords)
695         keywords = "[%s]" % (keywords)
696         ebuild = current_package.ebuild_path()
697         if ebuild:
698             pkgmask = _get_mask_status(current_package, True)
699             if is_package_masked:
700                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
701             elif pkgmask > 4:
702                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
703             elif pkgmask == 4 or pkgmask == 1:
704                 print (portage.output.brown("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
705             else:
706                 print (portage.output.green("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
707                 if "package.keywords" in filename:
708                     stable_list.append(str(current_package.cpv))
709                 if "package.accept_keywords" in filename:
710                     stable_listNg.append(str(current_package.cpv))
711
712 def handle_if_overlay(package):
713     overlay_text = ""
714     global print_overlay_flag
715     print_overlay_flag = True
716
717     ebuild_path,overlay_path = porttree.dbapi.findname2(str(package.cpv))
718     index = -1
719     try:
720         index = overlay_list.index(overlay_path)
721     except ValueError as error:
722         overlay_list.append(overlay_path)
723         index = overlay_list.index(overlay_path)
724
725 #    overlay_text = " [" + str(index+1) + "]"
726     overlay_text = " [%s]" % (str(index+1))
727
728     return overlay_text
729
730 # if the overlay_text was displayed to the user
731 # we need to display the string at the end 
732 # this array will store the overlays to be displayed
733 def print_overlay_text():
734    
735     global print_overlay_flag
736     
737     if (not print_overlay_flag):
738         return
739     
740     if (len(overlay_list) <= 0):
741         return
742
743     index = 1
744     for x in overlay_list:
745         print (portage.output.turquoise("[" + str(index) + "] ") + x)
746         index = index + 1
747     
748     print ("\n")
749
750 #helper function to print output
751 def print_output(log_level,output_string, package=None, filename=None):
752     
753     global logLevel
754
755     if package != None:
756         if (package.is_overlay()):
757             output_string = "%s%s" % (output_string,portage.output.turquoise(handle_if_overlay(package)))
758             #output_string = output_string + portage.output.turquoise(handle_if_overlay(package))
759         else:
760             if filename != None:
761                 output_string = "%s%s" % (output_string,portage.output.brown(" : " + filename))
762                 #output_string = output_string + portage.output.brown(" : " + filename)
763
764     if (log_level <= logLevel):
765         print (output_string)
766
767 # remove stabled files that are no longer needed from package.keywords
768 # or package.mask
769 # includes support for etc/portage/package.keywords/<whatever>/package.keywords
770 def cleanFile (filename):
771     
772     removeDups = []
773     removed_list = []
774
775     # if the file or directory does not exist
776     # exit out
777     if (os.path.exists(filename) == False):
778         return
779     
780     if "package.keywords" in filename:
781         if ( len(stable_list) == 0):
782             return
783         removeDups = stable_list
784     elif "package.accept_keywords" in filename:
785         if ( len(stable_listNg) == 0):
786             return
787         removeDups = stable_listNg
788     else:
789         if ( len(unmask_list) == 0):
790             return
791         removeDups = unmask_list
792
793     removedDict = {}
794
795     try:
796         # determine if filename is a directory
797         if os.path.isdir(filename):
798             # get listing of directory
799             filenames = os.listdir(filename)
800             for file_name in filenames:
801                 cleanFile(filename+os.path.sep+file_name)
802             return
803         else:   
804             #go through stable array and remove line if found
805             for line in fileinput.input(filename,inplace =1):
806                 itemFound = False
807                 line = line.strip()
808
809                 # make sure line is not empty and do not remove commented out lines
810                 if len(line) <= 0:
811                     continue
812                 elif line.find("#") == 0:
813                     print (line)
814                     continue
815
816                 for item in removeDups:
817                     if item in line:
818                         removed_list.append(item)
819                         removedDict[filename] = item
820                         itemFound = True
821                         removeDups.pop(removeDups.index(item))
822                         break
823                 if (itemFound == False):
824                     print (line)
825             fileinput.close()
826     except OSError as error:
827         print (portage.output.red("Modify/Read access to file: " + filename + " failed: ") ,error)
828     
829     if (len(removed_list) > 0):
830         print ("\n")
831         for package in removed_list:
832             print (portage.output.red("Removing from: ") + portage.output.yellow(filename) + ": " + portage.output.green(package) + "\n")
833
834 # main
835 if __name__ == "__main__":
836
837     if len(sys.argv) == 1:
838         print_usage()
839         sys.exit(1)
840
841     # soooooo stolen from emerge
842     tmpcmdline = sys.argv[1:]
843
844     for cmd in tmpcmdline:
845         if cmd[0:1] == "-" and cmd[1:2] != "-":
846             for cmd_item in cmd[1:]:
847                 if cmd_item in mappings:
848                     if mappings[cmd_item] in cmdline:
849                         print ()
850                         print ("*** Warning: Redundant use of ", mappings[cmd_item])
851                     else:
852                         cmdline.append(mappings[cmd_item])
853                 else:
854                     print ("!!! Error: -"+cmd_item+" is an invalid option.")
855                     sys.exit(-1)
856         else:
857             cmdline.append(cmd)
858
859     #parse long options
860     for cmd in cmdline:
861         if len(cmd)>=2 and cmd[0:2]=="--":
862             try:
863                 i = options.index(cmd)
864                 continue
865             except ValueError:
866                 print ("!!! Error: -"+cmd+" is an invalid option.")
867                 sys.exit(-1)
868
869     if "--changes-only" in cmdline:
870         cmdline.remove("--changes-only")
871         show_changes_only_flag = True
872
873     if "--removable-only" in cmdline:
874         cmdline.remove("--removable-only")
875         show_removable_only_flag = True
876
877     if "--debug" in cmdline:
878         logLevel = debug
879
880     if "--no-color" in cmdline:
881         portage.output.nocolor()
882
883     if "--tilde-check" in cmdline:
884         tilde=1
885
886     if "--version" in cmdline:
887         print_version()
888         sys.exit(0)
889     
890     if "--all" in cmdline:
891         tmpcmdline = ["--all"]
892         if "--fix" in cmdline:
893             tmpcmdline.append("--fix")
894         elif "--fixwithall" in cmdline:
895             tmpcmdline.append("--fixwithall")
896         cmdline=tmpcmdline      
897
898     if "--help" in cmdline:
899         print_usage()
900         sys.exit(0)
901     
902     if (show_changes_only_flag and show_removable_only_flag):
903         print ("Please select only one of --show-removable (-r) or --changes-only")
904         print ("Use --help for more info.")
905         sys.exit(0)
906         
907     for cmd in cmdline:
908         if cmd == "--keyword":
909             print (portage.output.bold("\npackage.keywords:"))
910             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
911             if "--fix" in cmdline:
912                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
913             print (portage.output.bold("Done\n"))
914             print (portage.output.bold("\npackage.accept_keywords:"))
915             get_recursive_info(USER_CONFIG_PATH + "/package.accept_keywords")
916             if "--fix" in cmdline:
917                 cleanFile(USER_CONFIG_PATH + "/package.accept_keywords")
918             print (portage.output.bold("Done\n"))
919         elif cmd == "--unmask": 
920             print (portage.output.bold("\npackage.unmask:"))
921             checking_package_unmask = True
922             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
923             checking_package_unmask = False
924             if "--fix" in cmdline:
925                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
926         elif cmd == "--mask":
927             checking_package_mask = True
928             print (portage.output.bold("\npackage.mask:"))
929             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
930             print (portage.output.bold("Done\n"))
931             checking_package_mask = False
932             if "--fix" in cmdline:
933                 cleanFile(USER_CONFIG_PATH + "/package.mask")
934         elif cmd == "--package-use":
935             print (portage.output.bold("\npackage.use:"))
936             processing_package_use = True
937             get_recursive_info(USER_CONFIG_PATH + "/package.use")
938             if "--fix" in cmdline:
939                 cleanFile(USER_CONFIG_PATH + "/package.use")
940             print (portage.output.bold("Done\n"))
941         elif cmd == "--all":
942             print (portage.output.bold("\npackage.keywords:"))
943             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
944             print (portage.output.bold("\npackage.accept_keywords:"))
945             get_recursive_info(USER_CONFIG_PATH + "/package.accept_keywords")
946             print (portage.output.bold("\npackage.unmask:"))
947             checking_package_unmask = True
948             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
949             checking_package_unmask = False
950             checking_package_mask = True
951             print (portage.output.bold("\npackage.mask:"))
952             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
953             print (portage.output.bold("\npackage.use:"))
954             processing_package_use = True
955             get_recursive_info(USER_CONFIG_PATH + "/package.use")
956             if "--fix" in cmdline:
957                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
958                 cleanFile(USER_CONFIG_PATH + "/package.accept_keywords")
959                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
960                 cleanFile(USER_CONFIG_PATH + "/package.use")
961                 cleanFile(USER_CONFIG_PATH + "/package.mask")
962             print (portage.output.bold("\nDone\n"))
963
964     print_overlay_text()
965
966     if len(cmdline) == 0:
967         if show_changes_only_flag or show_removable_only_flag:
968             if (show_changes_only_flag):
969                 print (portage.output.green("-c") + " or " + portage.output.green("--changes-only") + " must be accompanied by one of the following:")
970             else:
971                 print (portage.output.green("-r") + " or " + portage.output.green("--removable-only") + " must be accompanied by one of the following:")
972
973             print ("     " + portage.output.yellow("-k") + " or " + portage.output.yellow("--keyword"))
974             print ("     " + portage.output.yellow("-u") + " or " + portage.output.yellow("--unmask"))
975             print ("     " + portage.output.yellow("-m") + " or " + portage.output.yellow("--mask"))
976             print ("     " + portage.output.yellow("-a") + " or " + portage.output.yellow("--all"))
977
978             print_usage()
979