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