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