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