Addtional python3 porting
[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.1"
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     if mycp in settings.pmaskdict:
495         for package in settings.pmaskdict[mycp]:
496             if cpv in portage.portdb.xmatch("match-all", package):
497                 return True
498                 
499     return False    
500
501 # filter out keywords for archs other than the current one
502 def filter_keywords(keywords):
503     filtered_keywords = ""
504
505     #for key in key_list:
506     for key in keywords:
507         key = str.replace(key, "[", "")
508         key = str.replace(key, "]", "")
509         key = str.replace(key, ",", "")
510         arch=settings["ARCH"]
511         if key.rfind(arch) != -1:
512             if len(filtered_keywords) != 0:
513                 filtered_keywords = filtered_keywords + " "
514             filtered_keywords = filtered_keywords + key
515         elif "-*" in key:
516             if len(filtered_keywords) != 0:
517                 filtered_keywords = filtered_keywords + " "
518             filtered_keywords = filtered_keywords + key
519
520
521     return filtered_keywords
522
523 # check to see if we have a stable release
524 # in our package.* files that we can remove
525 def check_for_stable_release(pkg):
526     if not is_pkg_package_masked(str(pkg.cpv)):
527         status = _get_mask_status(pkg, True)
528         if status == 0:
529             return True
530     return False
531
532 #print version info
533 def print_version():
534     # Print the version of this tool to the console.
535     print (__productname__ + "(" + __version__ + ") - " + \
536         __description__)
537     print ("Author(s): " + __author__)
538
539 # function to go through a ~cp without a version
540 # and set for removal from file if no masked package version exists
541 def check_tilde_masked_pkg(package_name, filename):
542     ebuild_output=""
543     variable_version = ""
544
545     orig_package_name = package_name
546     package_name = package_name.replace('~','')
547
548     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: orig_package-name is " + orig_package_name))
549     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: package_name is " + package_name))
550
551     query = Query(package_name+"*", True)
552
553     packages = []
554     try:
555         packages = query.smart_find(True,True,True,True,False,True)
556     except errors.GentoolkitException as err:
557         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
558
559     no_versions_installed = True
560     for package in packages:
561         if package.is_installed():
562             no_versions_installed = False
563
564     if (no_versions_installed == True):
565         ebuild_output = portage.output.green("\n" + package_name + ": ") + portage.output.yellow("Not Installed")
566         if "package.unmask" in filename:
567             unmask_list.append(orig_package_name)
568         if "package.keywords" in filename:
569             stable_list.append(orig_package_name)
570         print (ebuild_output + portage.output.brown(" : " + filename))
571
572         return
573
574     # get all packages matching cat/package
575     query = Query(package_name+"*", True)
576     pkgs = []
577     try:
578         pkgs = query.smart_find(True,True,True,True,False,True)
579     except errors.GentoolkitException as err:
580         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
581
582     if (pkgs != None):
583         for current_package in pkgs:
584             print_output(debug,portage.output.blue("check_tilde_maskd_pkg: current_package is " + str(current_package.cpv)))
585             print_output(debug,portage.output.blue("comparing " + package_name + " to " + str(current_package.cpv)))
586             #if (compare_package_strings(package_name,str(current_package.cpv)) <=0):
587             if (pkgcmp(package_name, str(current_package.cpv)) <= 0):
588                 #if (pkgcmp(pkgsplit('test-1.0-r1'),pkgsplit('test-1.2-r3'))
589                 packageObj = gentoolkit.package.Package(str(current_package.cpv))
590                 if (packageObj == None):
591                     # we could not create a package object
592                     return
593                 pkgmask = _get_mask_status(packageObj, True)
594
595                 print_output(debug,portage.output.blue("check_tilde_maskd_pkg: current_package is " + str(current_package.cpv) + " and pkgmask is " + str(pkgmask)))
596
597                 if "package.unmask" in filename:
598                     if (pkgmask >= 3):
599                         # package was found as masked
600                         return 
601                     else:
602                         # package is not masked
603                         unmask_list.append(str(current_package.cpv))
604                         ebuild_output = portage.output.yellow(str(current_package.cpv)) + ": " + portage.output.yellow("Not package masked")
605                         print_output(info,ebuild_output, package, filename)
606                         return
607                 else:
608                     if (pkgmask >= 1):
609                         # package was found as masked
610                         return 
611                     else:
612                         # at this point we have no packages >= ~cpv that are masked, present for removal
613                         # package does not have any masked versions
614                         ebuild_output = portage.output.green(package_name + " has no masked versions")
615                         
616                         if "package.unmask" in filename:
617                             unmask_list.append(orig_package_name)
618                         if "package.keywords" in filename:
619                             stable_list.append(orig_package_name)
620                         print_output(info,ebuild_output, package, filename)
621
622 #helper function to print avail pks when version does not exist
623 def show_all_versions(pkg):
624
625     # is package masked
626     is_package_masked = False
627     pkgArr = portage.pkgsplit(pkg)
628
629     if pkgArr is None or len(pkgArr) == 0:
630         return
631
632     # determine if category/package is masked 
633     package = pkgArr[0]
634
635     operator = portage.dep.Atom(pkg).operator
636     if operator is not None:
637         package = package[len(operator):]
638
639     # package is category/package and pkg is category/package-version
640     # is category/package-version we are checking package masked?
641     #if portage.settings.pmaskdict.has_key(package):
642     if package in portage.settings.pmaskdict:
643         pkg_list = portage.settings.pmaskdict.get(package)
644         # iterate through list array looking for pkg
645         for pkg_check in pkg_list:
646             operator = portage.get_operator(pkg_check)
647             if operator is None:
648                 if pkg_check == package:
649                     is_package_masked = True
650
651     query = Query(package)
652
653     all_pkgs = []
654
655     try:
656         all_pkgs = query.smart_find(True,True,True,True,False,True)
657     except errors.GentoolkitException as err:
658         print_output(debug,portage.output.blue("Package " + package + " not found."))
659
660     for current_package in all_pkgs:
661         keywords = "%s" % (current_package.environment("KEYWORDS").split())
662         keywords = filter_keywords(keywords)
663         keywords = "[" + keywords + "]"
664         ebuild = current_package.ebuild_path()
665         if ebuild:
666             pkgmask = _get_mask_status(current_package, True)
667             if is_package_masked:
668                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
669             elif pkgmask > 4:
670                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
671             elif pkgmask == 4 or pkgmask == 1:
672                 print (portage.output.brown("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
673             else:
674                 print (portage.output.green("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
675                 stable_list.append(str(current_package.cpv))
676
677 def handle_if_overlay(package):
678     overlay_text = ""
679     global print_overlay_flag
680     print_overlay_flag = True
681
682     ebuild_path,overlay_path = porttree.dbapi.findname2(str(package.cpv))
683     index = -1
684     try:
685         index = overlay_list.index(overlay_path)
686     except ValueError as error:
687         overlay_list.append(overlay_path)
688         index = overlay_list.index(overlay_path)
689
690     overlay_text = " [" + str(index+1) + "]"
691
692     return overlay_text
693
694 # if the overlay_text was displayed to the user
695 # we need to display the string at the end 
696 # this array will store the overlays to be displayed
697 def print_overlay_text():
698    
699     global print_overlay_flag
700     
701     if (not print_overlay_flag):
702         return
703     
704     if (len(overlay_list) <= 0):
705         return
706
707     index = 1
708     for x in overlay_list:
709         print (portage.output.turquoise("[" + str(index) + "] ") + x)
710         index = index + 1
711     
712     print ("\n")
713
714 #helper function to print output
715 def print_output(log_level,output_string, package=None, filename=None):
716     
717     global logLevel
718
719     if package != None:
720         if (package.is_overlay()):
721             output_string = output_string + portage.output.turquoise(handle_if_overlay(package))
722         else:
723             if filename != None:
724                 output_string = output_string + portage.output.brown(" : " + filename)
725
726     if (log_level <= logLevel):
727         print (output_string)
728
729 # remove stabled files that are no longer needed from package.keywords
730 # or package.mask
731 # includes support for etc/portage/package.keywords/<whatever>/package.keywords
732 def cleanFile (filename):
733     
734     removeDups = []
735     removed_list = []
736
737     # if the file or directory does not exist
738     # exit out
739     if (os.path.exists(filename) == False):
740             return
741     
742     if "package.keywords" in filename:
743         if ( len(stable_list) == 0):
744             return
745         for i in stable_list:
746             if not removeDups.count(i):
747                 removeDups.append(i)
748     else:
749         if ( len(unmask_list) == 0):
750             return
751         for i in unmask_list:
752             if not removeDups.count(i):
753                 removeDups.append(i)
754
755     removedDict = {}
756
757     try:
758         # determine if filename is a directory
759         if os.path.isdir(filename):
760             # get listing of directory
761             filenames = os.listdir(filename)
762             for file_name in filenames:
763                 cleanFile(filename+os.path.sep+file_name)
764         else:   
765             #go through stable array and remove line if found
766             for line in fileinput.input(filename,inplace =1):
767                 itemFound = False
768                 line = line.strip()
769
770                 # make sure line is not empty and do not remove commented out lines
771                 if len(line) <= 0:
772                     continue
773                 elif line.find("#") == 0:
774                     print (line)
775                     continue
776
777                 for item in removeDups:
778                     if item in line:
779                         dup_found = False
780                         for check_item in removed_list:
781                             if (check_item == item):
782                                 dup_found = True
783                         if (dup_found == False):
784                             removed_list.append(item)
785                             removedDict[filename] = item
786                             itemFound = True
787                 if (itemFound == False):
788                     print (line)
789             fileinput.close()
790     except OSError as error:
791         print (portage.output.red("Modify/Read access to file: " + filename + " failed: ") ,error)
792     
793     if (len(removed_list) > 0):
794         print ("\n")
795         for package in removed_list:
796             print (portage.output.red("Removing from: ") + portage.output.yellow(filename) + ": " + portage.output.green(package) + "\n")
797
798
799 # thanks to Paul Varner (Fuzzyray)
800 #Returns the list ordered in the same way portage 
801 #would do with lowest version at the head of the list.
802 def sort_package_list(pkglist):
803     from gentoolkit.package import Package
804     pkglist.sort()
805     return pkglist
806             
807 # main
808 if __name__ == "__main__":
809
810     if len(sys.argv) == 1:
811         print_usage()
812         sys.exit(1)
813
814     # soooooo stolen from emerge
815     tmpcmdline = sys.argv[1:]
816
817     for cmd in tmpcmdline:
818         if cmd[0:1] == "-" and cmd[1:2] != "-":
819             for cmd_item in cmd[1:]:
820                 if cmd_item in mappings:
821                     if mappings[cmd_item] in cmdline:
822                         print ()
823                         print ("*** Warning: Redundant use of ", mappings[cmd_item])
824                     else:
825                         cmdline.append(mappings[cmd_item])
826                 else:
827                     print ("!!! Error: -"+cmd_item+" is an invalid option.")
828                     sys.exit(-1)
829         else:
830             cmdline.append(cmd)
831
832     #parse long options
833     for cmd in cmdline:
834         if len(cmd)>=2 and cmd[0:2]=="--":
835             try:
836                 i = options.index(cmd)
837                 continue
838             except ValueError:
839                 print ("!!! Error: -"+cmd+" is an invalid option.")
840                 sys.exit(-1)
841
842     if "--changes-only" in cmdline:
843         cmdline.remove("--changes-only")
844         show_changes_only_flag = True
845
846     if "--removable-only" in cmdline:
847         cmdline.remove("--removable-only")
848         show_removable_only_flag = True
849
850     if "--debug" in cmdline:
851         logLevel = debug
852
853     if "--no-color" in cmdline:
854         portage.output.nocolor()
855
856     if "--tilde-check" in cmdline:
857         tilde=1
858
859     if "--version" in cmdline:
860         print_version()
861         sys.exit(0)
862     
863     if "--all" in cmdline:
864         tmpcmdline = ["--all"]
865         if "--fix" in cmdline:
866             tmpcmdline.append("--fix")
867         elif "--fixwithall" in cmdline:
868             tmpcmdline.append("--fixwithall")
869         cmdline=tmpcmdline      
870
871     if "--help" in cmdline:
872         print_usage()
873         sys.exit(0)
874     
875     if (show_changes_only_flag and show_removable_only_flag):
876         print ("Please select only one of --show-removable (-r) or --changes-only")
877         print ("Use --help for more info.")
878         sys.exit(0)
879         
880     for cmd in cmdline:
881         if cmd == "--keyword":
882             print (portage.output.bold("\npackage.keywords:"))
883             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
884             if "--fix" in cmdline:
885                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
886             print (portage.output.bold("Done\n"))
887         elif cmd == "--unmask": 
888             print (portage.output.bold("\npackage.unmask:"))
889             checking_package_unmask = True
890             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
891             checking_package_unmask = False
892             if "--fix" in cmdline:
893                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
894         elif cmd == "--mask":
895             checking_package_mask = True
896             print (portage.output.bold("\npackage.mask:"))
897             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
898             print (portage.output.bold("Done\n"))
899             checking_package_mask = False
900             if "--fix" in cmdline:
901                 cleanFile(USER_CONFIG_PATH + "/package.mask")
902         elif cmd == "--package-use":
903             print (portage.output.bold("\npackage.use:"))
904             processing_package_use = True
905             get_recursive_info(USER_CONFIG_PATH + "/package.use")
906             if "--fix" in cmdline:
907                 cleanFile(USER_CONFIG_PATH + "/package.use")
908             print (portage.output.bold("Done\n"))
909         elif cmd == "--all":
910             print (portage.output.bold("\npackage.keywords:"))
911             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
912             print (portage.output.bold("\npackage.unmask:"))
913             checking_package_unmask = True
914             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
915             checking_package_unmask = False
916             checking_package_mask = True
917             print (portage.output.bold("\npackage.mask:"))
918             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
919             print (portage.output.bold("\npackage.use:"))
920             processing_package_use = True
921             get_recursive_info(USER_CONFIG_PATH + "/package.use")
922             if "--fix" in cmdline:
923                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
924                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
925                 cleanFile(USER_CONFIG_PATH + "/package.use")
926                 cleanFile(USER_CONFIG_PATH + "/package.mask")
927             print (portage.output.bold("\nDone\n"))
928
929     print_overlay_text()
930
931     if len(cmdline) == 0:
932         if show_changes_only_flag or show_removable_only_flag:
933             if (show_changes_only_flag):
934                 print (portage.output.green("-c") + " or " + portage.output.green("--changes-only") + " must be accompanied by one of the following:")
935             else:
936                 print (portage.output.green("-r") + " or " + portage.output.green("--removable-only") + " must be accompanied by one of the following:")
937
938             print ("     " + portage.output.yellow("-k") + " or " + portage.output.yellow("--keyword"))
939             print ("     " + portage.output.yellow("-u") + " or " + portage.output.yellow("--unmask"))
940             print ("     " + portage.output.yellow("-m") + " or " + portage.output.yellow("--mask"))
941             print ("     " + portage.output.yellow("-a") + " or " + portage.output.yellow("--all"))
942
943             print_usage()
944