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