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