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