Fix redundant messaging
[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.1.18"
20 __productname__ = "portpeek"
21 __description__ = "Displays user unmasked ebuilds and installable options from the portage tree"
22
23 import sys, os, portage, fileinput, re, gentoolkit
24 from gentoolkit.versionmatch import VersionMatch,errors
25 from portage.const import USER_CONFIG_PATH
26 from portage.versions import catpkgsplit,pkgcmp,pkgsplit
27 from portage.exception import InvalidAtom
28 from gentoolkit.cpv import CPV
29 from gentoolkit.package import Package
30 from gentoolkit.query import Query
31 from gentoolkit.flag import get_iuse
32
33 # support python 2
34 try:
35     input = raw_input
36 except NameError:
37     pass
38
39 porttree = portage.db[portage.root]["porttree"]
40 settings = portage.config(clone=portage.settings)
41 #settings = portage.config(local_config=False)
42
43 show_changes_only_flag = False
44 checking_package_unmask = False
45 checking_package_mask = False
46 print_overlay_flag = False
47 info = 0
48 debug = 1
49 logLevel = info
50 show_removable_only_flag = False
51 stable_list = []
52 stable_listNg = [] # handle package.accept_keywords
53 unmask_list = []
54 tilde = 0
55 processing_package_use = False
56 using_gentoo_as_overlay = False
57 overlay_list = []
58 fix_confirm = True
59 fix_asked = False
60 use_flag_dict = {}
61 useremove_display = ""
62 invalid_flag_found = False
63
64 try:
65     PORTAGE_CONFIGROOT
66 except NameError:
67     PORTAGE_CONFIGROOT="/"
68
69 USER_CONFIG_PATH=PORTAGE_CONFIGROOT + USER_CONFIG_PATH
70
71 #parameters
72 options = [
73 "--keyword",
74 "--unmask",
75 "--mask",
76 "--all",
77 "--changes-only",
78 "--version",
79 "--version",
80 "--help",
81 "--removable-only",
82 "--debug",
83 "--fix", 
84 "--tilde-check",
85 "--no-color",
86 "--package-use",
87 "--fix-confirm"
88 ]
89
90 mappings = {
91 "k":"--keyword",
92 "u":"--unmask",
93 "m":"--mask",
94 "a":"--all",
95 "c":"--changes-only",
96 "V":"--version",
97 "v":"--version",
98 "h":"--help",
99 "r":"--removable-only",
100 "d":"--debug",
101 "f":"--fix", 
102 "t":"--tilde-check",
103 "n":"--no-color",
104 "s":"--package-use",
105 "q":"--fix-confirm"
106 }
107
108 cmdline = []
109 overlays = [settings["PORTDIR_OVERLAY"]]
110
111 def print_usage():
112     # Print full usage information for this tool to the console.
113     print ("\nUsage: " + portage.output.turquoise(__productname__) +  portage.output.yellow(" command "))
114     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [ options ]") +  portage.output.yellow(" command "))
115     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-c]") + portage.output.yellow(" [akmu]"))
116     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-r]") + portage.output.yellow(" [akmu]"))
117     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-f]") + portage.output.yellow(" [akmu]"))
118     print ("       " + portage.output.turquoise(__productname__) + portage.output.green(" [-F]") + portage.output.yellow(" [akmu]"))
119     print (portage.output.yellow(" command ") + " can be ")
120     print (portage.output.yellow(" -a, --all") + "       - show all matches")
121     print (portage.output.yellow(" -k, --keyword") + "       - show matches from package.keywords and package.accept_keywords only")
122     print (portage.output.yellow(" -m, --mask") + "      - show matches from package.mask only")
123     print (portage.output.yellow(" -u, --unmask") + "        - show matched from package.unmask only")
124     print (portage.output.yellow(" -s, --package.use") + "   - show matches from package.use only")
125     
126     print (portage.output.yellow(" -f, --fix") + "       - will remove the stabled and invalid packages without asking for confirmation")
127     print (portage.output.yellow(" -q, --confirm-fix") +"    - will remove the stabled and invalid packages asking for confirmation before doing so")
128     print (portage.output.yellow(" -h, --help") + "      - display this message")
129     print (portage.output.yellow(" -d, --debug") + "         - display more verbose output for debugging")
130     print (portage.output.yellow(" -V, --version") + "       - display version info")
131     print (portage.output.green("options") + " are ")
132     print (portage.output.green(" -c, --changes-only") + \
133         "   - show all matches that have upgrade option, use with " + \
134         "<" + portage.output.yellow(" k ") + "|" + portage.output.yellow(" u ") + \
135         "|" + portage.output.yellow(" m ") + "|" + \
136         portage.output.yellow(" a ") + ">")
137     print (portage.output.green(" -n, --no-color") + \
138         "   - suppress color output")
139     print (portage.output.green(" -r, --removable-only") + \
140         "   - show all matches that can be removed from package files, use with " + \
141         "<" + portage.output.yellow(" k ") + "|" + portage.output.yellow(" u ") + \
142         "|" + portage.output.yellow(" m ") + "|" + \
143         portage.output.yellow(" a ") + ">\n")
144
145 def get_keywords(package, var):
146     mytree = porttree
147     filtered_keywords = ""
148     try:
149         keywords = package.environment("KEYWORDS").split()
150     except KeyError as error:
151         print ("!!! Portpeek caught Exception:" + format(error))
152         print ("!!! This package/version seems to be no longer available, " + \
153         "please check and update/unmerge it") 
154         return "Not Available/Deprecated"
155     
156     #filtered_keywords = filter_keywords(keywords[0])
157     filtered_keywords = filter_keywords(keywords)
158     return filtered_keywords
159
160
161
162 # this is the main function for portpeek
163 # TODO comment this code!
164 def parse_line(line, filename):     
165     global info,debug,using_gentoo_as_overlay
166
167     using_gentoo_as_overlay = False
168     pkgs = None
169     ebuild_output = ""
170     check_pkg = ""
171     not_installed_pkgs = 0
172     pkg_length = 0
173     atom_check="<>=~"
174     original_line = line
175     has_asterick = False
176
177     # if the line has special characters, we need to make sure the original line is used for matching
178     special_line = False
179
180     pattern=r'[<>:*]'
181
182     if ( (re.search(pattern,line) != None) ):
183         special_line = True
184
185     if ( line.find("*") > 0 ) or ( line.find("<") >= 0 ):
186         has_asterick = True
187
188     print_output(debug,portage.output.blue("Analyzing line: " + line))
189
190     diffs_found = False
191     display_done = False
192
193     fields = line.replace("\t", " ").split(" ")
194
195     if len(fields) > 0:
196         #if the line does not start with an atom such as (= or >), do not validate 
197         #status as this tool is for check specific versions and not entire packages 
198         # a ~cpv should be handled like an =cpv if requested bythe parameter -t
199         check_pkg = fields[0] # this should be one of <>=~
200         orig_pkg_name = check_pkg
201         overlay_index = check_pkg.find("::")
202         if ( overlay_index >= 0):
203             overlay_list = check_pkg.rsplit("::")
204             if (len(overlay_list) > 0):
205                 overlay_name = overlay_list[1]
206                 if (overlay_name == "gentoo"):
207                     using_gentoo_as_overlay = True
208             check_pkg = check_pkg[0:check_pkg.find("::")]
209
210         if check_pkg[0] not in atom_check:
211
212             # if this is package_unmask, then check the non-atom containing package to see if any version is masked
213
214             if (checking_package_unmask == True):
215                 
216                 print_output(debug,portage.output.blue("check_pkg is " + check_pkg + " found"))
217                 if (is_any_cpv_keyword_masked(check_pkg) == False):
218                     print_output(info,portage.output.red("No masked versions of " + check_pkg + " found. Tagged for removal."))
219                     unmask_list.append(str(check_pkg))
220             return
221         if (tilde == 1):
222             if check_pkg[0] in "~":
223                 check_tilde_masked_pkg(check_pkg, filename)
224                 return
225
226         # determine if the package exists
227         try:
228             package_exists = portage.portdb.xmatch("match-all", fields[0])
229         except InvalidAtom:
230             package_exists = False
231
232         if package_exists:
233             # return a Package List based on the cpv
234             query = Query(check_pkg)
235             #query = Query(fields[0])
236             pkgs = []
237
238             try:
239                 pkgs = query.smart_find(True,True,True,True,False,True)
240             except errors.GentoolkitException as err:
241                 pass
242
243             if (pkgs != None):
244                 pkg_length = len(pkgs)
245                 not_installed_pkgs = 0
246                 display_done = False
247
248                 # go through each package version for a specific version found above
249                 for current_package in pkgs:
250                     if not current_package.is_installed():
251
252                         # we have found a package that is in our file, but not installed
253                         not_installed_pkgs = not_installed_pkgs + 1
254
255                         # check to see if specific version of pkg is not installed 
256                         # and display if true
257                         check_pkg = fields[0]
258                         if check_pkg[0] in atom_check:
259                             check_pkg = check_pkg[1:]
260
261                         if (check_pkg == str(current_package.cpv)):
262                             if (not checking_package_mask):
263                                 # package is not installed
264                                 print_output(info,portage.output.yellow("\n" + orig_pkg_name + ": ") + portage.output.red("Not Installed") , current_package, filename)
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                                 unmask_list.append(str(current_package.cpv))
270                             else:
271                                 # package is masked, and not installed, this is normal use of package.mask
272                                 print_output(info,portage.output.green("" + orig_pkg_name + ": ") + portage.output.yellow("Package Masked"),current_package, filename)
273                             display_done = True
274                         continue
275
276                     # package is installed
277                     # retrieve the keywords for a file
278                     keywords = "%s" % (get_keywords(current_package,"KEYWORDS").split())
279                     if (keywords.find("Available/Deprecated") >= 0):
280                         continue
281
282                     #retrieve the mask status of a specific package
283                     pkgmask = _get_mask_status(current_package, False)
284
285                     #determine if installed package is unmasked, if so, display keywords as green
286                     stable = check_for_stable_release(current_package)
287
288                     # do not display if keywords don't exist
289                     if keywords == "[]":
290                         continue
291
292                     if stable:
293                         if ( not has_asterick): # if it has an asterick, then we don't remove
294                             ebuild_output = portage.output.green("Installed: ") + \
295                                 portage.output.turquoise(orig_pkg_name) + \
296                                 portage.output.green("  Keywords " + keywords)
297                             if "package.unmask" in filename:
298                                 unmask_list.append(str(current_package.cpv))
299                             if "package.keywords" in filename:
300                                 stable_list.append(str(current_package.cpv))
301                             if "package.accept_keywords" in filename:
302                                 stable_listNg.append(str(current_package.cpv))
303                     else:
304                         if (not show_removable_only_flag):
305                             if (not checking_package_unmask):
306                                 ebuild_output = portage.output.green("Installed: ") + \
307                                 portage.output.turquoise(orig_pkg_name) + \
308                                 portage.output.yellow("  Keywords " + keywords) 
309                         else:
310                             ebuild_output = portage.output.yellow(str(current_package.cpv))
311
312                     # check package.unmask
313                     if (checking_package_unmask):
314                         if (not is_any_cpv_file_masked(str(current_package.cpv))):
315
316                             # package is in package.unmask unnecessarily
317                             ebuild_output = portage.output.yellow("\n" + orig_pkg_name  + ": ") + portage.output.green("Not package masked")
318                             if "package.unmask" in filename:
319                                 unmask_list.append(str(current_package.cpv))
320                                 print_output (info, "" +  ebuild_output,None, filename)
321                                 continue
322
323                     # print once
324                     ebuild_search_key_printed = False
325                     if ( not has_asterick): # if it has an asterick, then we don't remove
326                         if stable:
327                             diffs_found = False
328                             ebuild_search_key_printed = True
329                             print_output(info,"\n" + ebuild_output,current_package, filename)
330                         elif not show_changes_only_flag and not show_removable_only_flag:
331                             diffs_found = False
332                             ebuild_search_key_printed = True
333                             print_output(info,"\n" + ebuild_output,current_package)
334
335                     # go through all versions of a package
336                     query = Query(current_package.category + "/" + current_package.name)
337
338                     all_pkgs = []
339
340                     try:
341                         all_pkgs = query.smart_find(True,True,True,True,False,True)
342                     except errors.GentoolkitException as err:
343                         print_output(debug,portage.output.blue("Package " + current_package.category + "/" + current_package.name + " not found."))
344
345                     for a_package in all_pkgs:
346                         if not a_package.is_installed():
347                             # a_package is not installed
348                             pkgmask = _get_mask_status(a_package, False)
349                             # print status line of package we are now checking
350                             print_output(debug,portage.output.blue("Checking package: " + str(a_package.cpv) +".pkgmask is " + str(pkgmask)))
351                             # if package versions are different
352                             if (VersionMatch(CPV(current_package.cpv)).match(CPV(a_package.cpv))):
353                                 diffs_found = True
354                                 keylist = a_package.environment("KEYWORDS")
355                                 keywords = "%s" % (filter_keywords(keylist)).split()
356                                 #if a_package is masked
357                                 if pkgmask > 0 and not show_removable_only_flag:
358                                     if show_changes_only_flag and not ebuild_search_key_printed:
359                                         print_output (info, "\n" +  ebuild_output, current_package)
360                                         ebuild_search_key_printed = True
361                                         check_for_stable_release(current_package)
362                                     if (pkgmask >= 3):
363                                         print_output (info,portage.output.red("Available: " + str(a_package.cpv) + " [M] Keywords: " + keywords),a_package)
364                                     else:
365                                         print_output (info,portage.output.brown("Available: " + str(a_package.cpv) + " Keywords: " + keywords),a_package)
366                                 else:
367                                     if show_changes_only_flag and not ebuild_search_key_printed:
368                                         print_output (info,"\n" + ebuild_output,current_package)
369                                         ebuild_search_key_printed = True
370                                         print_output(info,portage.output.green("Available: " + str(a_package.cpv) + " Keywords: " + keywords),a_package)
371                 #else:
372                     #print (portage.output.red ("\nCannot find package: " + check_pkg))
373
374                 # if package does not exist, and current_package is None
375                 # then make package using fields[0]
376
377                 # display if pkg/cat is not installed (missing version)
378                 if not_installed_pkgs ==  pkg_length:
379                     if not display_done:
380                         if (not checking_package_mask):
381                             print_output(info,portage.output.yellow("\n" + fields[0] + ": ") + portage.output.red("Not Installed"),current_package)
382                             if "package.keywords" in filename:
383                                 if (special_line == False):
384                                     stable_list.append(str(current_package.cpv))
385                                 else:
386                                     stable_list.append(original_line)
387                             if "package.accept_keywords" in filename:
388                                 if (special_line == False):
389                                     stable_listNg.append(str(current_package.cpv))
390                                 else:
391                                     stable_listNg.append(original_line)
392                             if (special_line == False):
393                                 unmask_list.append(str(current_package.cpv))
394                             else:
395                                 unmask_list.append(original_line)
396                         else:
397                             print_output (info,portage.output.green("\n" + str(current_package.cpv) + ": ") + portage.output.yellow("Package Masked"),current_package)
398         else:
399             diffs_found = True
400             if "package.keywords" in filename:
401                 stable_list.append(fields[0])
402                 print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
403             if "package.accept_keywords" in filename:
404                 stable_listNg.append(fields[0])
405                 print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
406
407             if "package.unmask" in filename:
408                 package_name = fields[0]
409                 if package_name[0] in atom_check:
410                     package_name = package_name[1:]
411                 port = Package(package_name)
412                 # if package is not installed remove it
413                 if ((not port.is_installed())):
414                     print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
415                     unmask_list.append(fields[0])
416
417             show_all_versions(fields[0], filename)
418     current_package = ""
419
420     return diffs_found
421
422 # adding support for etc/portage/package.keywords/<whatever>/package.keywords
423 def get_recursive_info(filename):
424
425     # determine if filename is a directory
426     if os.path.isdir(filename):
427         # get listing of directory
428         filenames = os.listdir(filename)
429         for file_name in filenames:
430             get_recursive_info(filename+os.path.sep+file_name)
431     else:   
432         get_info(filename)
433
434 def get_info(filename):
435
436     diffs_found = False
437     no_file = False
438     filedescriptor = None
439
440     try:
441         filedescriptor = open(filename)
442         for line in filedescriptor.readlines():
443             line = line.strip()
444             if len(line) <= 0:
445                 continue
446             elif line.find("#") >= 0:
447                 # found '#' remove comment
448                 if (skipFile(line,filename)):
449                     return
450                 line = line[0:line.find("#")]
451                 line = line.strip()
452                 if len(line) <= 0:
453                     continue
454             if (processing_package_use == False):
455                 diffs_found = parse_line(line, filename)
456             else:
457                 # process package.use
458                 diffs_found = parse_package_use(line,filename)
459     except IOError:
460         print (portage.output.red("Could not find file " + filename))
461         no_file = True
462
463     if not diffs_found and no_file:
464         print (portage.output.brown("No ebuild options found."))
465
466     # close file
467     if (filedescriptor != None):
468         filedescriptor.close()
469
470 # parse the package.use file and look for packages
471 # not installed
472 def parse_package_use(line, filename):
473
474     global info,debug
475     print_output(debug,portage.output.blue("parse_package_use: Line: " + line))
476     pkgs = None
477     check_pkg = ""
478     pkg_length = 0
479     atom_check="<>="
480     any_version = False
481     has_atom = True
482
483     diffs_found = False
484     package_installed = False
485     fields = line.replace("\t", " ").split(" ")
486     less_than = False
487     less_than_or_equal = False
488
489     if len(fields) > 0:
490         check_pkg = fields[0] # this could be one of <>=
491         orig_pkg_name = check_pkg
492         if check_pkg[0] not in atom_check:
493             has_atom = False
494         else:
495             if check_pkg[0] == '<':
496                 less_than = True;
497                 if check_pkg[1] == '=':
498                     less_than_or_equal = True
499
500             check_pkg = check_pkg[1:]
501             if check_pkg[0] in atom_check:
502                 check_pkg = check_pkg[1:]
503
504
505         # if there is a wildcard, check to make sure at least one
506         # of the packages is installed
507         if check_pkg.find("/*") >= 0:
508             query = Query(check_pkg)
509             pkgs = []
510             try:
511                 pkgs = query.smart_find(True,True,True,True,False,True)
512             except errors.GentoolkitException as err:
513                 print_output(debug,portage.output.blue("parse_package_use: Package " + check_pkg + " not found."))
514                 return False
515
516             if (pkgs != None):
517                 pkg_length = len(pkgs)
518             for current_package in pkgs:
519                 # on wildcard scenario, return False if one package is installed
520                 if current_package.is_installed():
521                     check_useflags(current_package,line)
522                     return False
523         check_pkg = (check_pkg.rsplit(':',1))[0]
524         
525         if ((orig_pkg_name.find("<=") >=0) or (orig_pkg_name.find("<")>=0) or (orig_pkg_name.find(">=") >=0) or (orig_pkg_name.find(">") >=0)):
526             query = Query(check_pkg)
527             pkgs = []
528             try:
529                 pkgs = query.smart_find(True,True,True,True,False,True)
530             except errors.GentoolkitException as err:
531                 print_output(debug,portage.output.blue("parse_package_use: Package " + check_pkg + " not found."))
532                 return False
533
534             if (pkgs != None):
535                 pkg_length = len(pkgs)
536             for current_package in pkgs:
537                 # on wildcard scenario, return False if one package is installed
538                 if (orig_pkg_name.find("<=") >=0):
539                     if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) <= 0):
540                         if current_package.is_installed():
541                             check_useflags(current_package,line)
542                         return False
543                 else:
544                     if (orig_pkg_name.find("<") >=0):
545                         if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) < 0):
546                             if current_package.is_installed():
547                                 check_useflags(current_package,line)
548                             return False
549                 if (orig_pkg_name.find(">=") >=0):
550                     if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) == 0) or (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) == 2):
551                         if current_package.is_installed():
552                             check_useflags(current_package,line)
553                         return False
554                 else:
555                     if (orig_pkg_name.find(">") >=0):
556                         if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) == 2):
557                             if current_package.is_installed():
558                                 check_useflags(current_package,line)
559                             return False
560         else:
561             # look for any version of check_pkg installed as there is 
562             # no version specified in package.use
563             package_exists = portage.portdb.xmatch("match-all", check_pkg)
564             if package_exists:
565                 # get all package versions
566                 query = Query(check_pkg)
567                 pkgs = []
568                 try:
569                     pkgs = query.smart_find(True,True,True,True,False,True)
570                 except errors.GentoolkitException as err:
571                     print_output(debug,portage.output.blue("Package " + check_pkg + " not found."))
572     
573                 if (pkgs != None):
574                     pkg_length = len(pkgs)
575     
576                     # go through each package version for a specific 
577                     # version found above
578                     # if one line check returns all ok, we don't need 
579                     #to check the rest. One slot could have different 
580                     #use flags than another
581                     if (has_atom == False):
582                         check_useflags_all_versions(pkgs, line, check_pkg)
583                         package_installed = check_for_any_installed_version(pkgs)
584                         current_package = None
585
586                         #for current_package in pkgs:
587                         #    if current_package.is_installed():
588                         #        check_useflags(current_package,line)
589                         #        package_installed = True
590                         #        if (invalid_flag_found == False):
591                         #            break;
592                     else: 
593                         # go through each package version for a specific version found above
594                         for current_package in pkgs:
595                             if (str(current_package.cpv) == check_pkg):
596                                 if not current_package.is_installed():
597                                     print_output(info,portage.output.yellow("\n" + orig_pkg_name + ": ") + portage.output.red("Not Installed"),current_package)
598                                     if "package.keywords" in filename:
599                                         stable_list.append(check_pkg)
600                                     if "package.accept_keywords" in filename:
601                                         stable_listNg.append(check_pkg)
602                                     unmask_list.append(check_pkg)
603                                     if "package.use" in filename:
604                                         valid_flag_list = []
605                                         valid_flag_list.insert(0,current_package);
606                                         use_flag_dict[line] = valid_flag_list
607                                         check_for_change = use_flag_dict[line]
608                                     return True
609                                 else:
610                                     check_useflags(current_package,line)
611                                     return False
612             else:
613                 print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
614                 if "package.keywords" in filename:
615                     stable_list.append(check_pkg)
616                 if "package.accept_keywords" in filename:
617                     stable_listNg.append(check_pkg)
618                 unmask_list.append(check_pkg)
619                 if "package.use" in filename:
620                     valid_flag_list = []
621                     #valid_flag_list.insert(0,current_package);
622                     valid_flag_list.insert(0,fields[0]);
623                     use_flag_dict[line] = valid_flag_list
624                     check_for_change = use_flag_dict[line]
625                 return True
626
627     if (package_installed == False):
628         # package does not exists
629         print_output(info,portage.output.yellow("\n" + orig_pkg_name + ": ") + portage.output.red(" Not Installed"),current_package)
630         if "package.keywords" in filename:
631             stable_list.append(check_pkg)
632         if "package.accept_keywords" in filename:
633             stable_listNg.append(check_pkg)
634         if "package.use" in filename:
635             valid_flag_list = []
636             valid_flag_list.insert(0,current_package);
637             use_flag_dict[line] = valid_flag_list
638             check_for_change = use_flag_dict[line]
639
640         unmask_list.append(check_pkg)
641         return True
642
643     return False
644
645
646
647
648 # skip the file if portpeek-skip found in a comment
649 # you can put this in the middle of a file and it will
650 #skip all entrie below it, this is useful to speed things
651 #up by not checking ~kde versions, for example
652 def skipFile (line, filename):
653     if ( (line == None) or (filename == None)):
654         return False
655
656     if line.find("portpeek-skip") >= 0:
657         return True
658
659     return False
660
661 # parts blatantly stolen from equery
662 # if pure is true, then get "true" mask status that is
663 # not affected by entries in /etc/portage/package.*
664 def _get_mask_status(pkg, pure):
665     pkgmask = 0
666
667     if (pkg == None):
668         return 0
669
670     if pkg.is_masked():
671         pkgmask = pkgmask + 3
672
673     if pure:
674         try:
675             keywords = portage.portdb.aux_get(str(pkg.cpv), ["KEYWORDS"])
676             keywords = keywords[0].split()
677         except KeyError:
678             # cpv does not exist
679             return 0
680     else:
681         keywords = pkg.environment("KEYWORDS").split()
682
683     # first check for stable arch, stop there if it is found
684     if settings["ARCH"] in keywords:
685         return 0
686
687     if "~" + settings["ARCH"] in keywords:
688         pkgmask = pkgmask + 1
689     elif "-*" in keywords or "-" + settings["ARCH"] in keywords:
690         pkgmask = pkgmask + 2
691     
692     return pkgmask
693
694 def is_pkg_package_masked(cpv):
695
696     print_output(debug,portage.output.blue("is_pkg_package_masked called: " + cpv))
697     settings2 = portage.config(local_config=False)
698     portdb2 = portage.portdbapi(None, settings)
699
700     mask_reason_list = None
701     
702     try:
703         for mask_reason_list in portage.getmaskingstatus(cpv, settings2, portdb=portdb2):
704             return True
705     except:
706         return False
707
708     return False
709
710 # filter out keywords for archs other than the current one
711 def filter_keywords(keywords):
712     filtered_keywords = ""
713
714     #for key in key_list:
715     for key in keywords:
716         key = key.replace("[", '')
717         key = key.replace("]", "")
718         key = key.replace(",", "")
719         arch=settings["ARCH"]
720
721         # remove '~' from key for comparison
722         key_comparison = key.lstrip('~')
723         if key_comparison == arch:
724             if len(filtered_keywords) != 0:
725                 filtered_keywords = filtered_keywords + " "
726             filtered_keywords = filtered_keywords + key
727         elif "-*" in key:
728             if len(filtered_keywords) != 0:
729                 filtered_keywords = filtered_keywords + " "
730             filtered_keywords = filtered_keywords + key
731
732
733     return filtered_keywords
734
735
736 # this function takes in a package with no cpv
737 # and returns True if any version is masked
738 def is_any_cpv_keyword_masked(package_name):
739     print_output(debug,portage.output.blue("inside is_any_cpv_keyword_masked: " + package_name))
740
741     query = Query(package_name, True)
742
743     packages = []
744     try:
745         packages = query.smart_find(True,True,True,True,False,True)
746     except errors.GentoolkitException as err:
747         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
748
749     for package in packages:
750         if is_pkg_package_masked(package.cpv):
751             print_output(debug,portage.output.blue("Package " + package.cpv + " is masked."))
752             return True
753
754     print_output(debug,portage.output.blue("Package " + package_name + ". No masked versions."))
755     return False
756
757 # we need to know if the package is masked
758 # by being present in the package.mask file
759 # as opposed to masked by keyword.
760 # if it's not, then we can remove from package.unmask
761 def is_any_cpv_file_masked(package_name):
762
763     print_output(debug,portage.output.blue("is_any_cpv_file_masked called: " + package_name))
764     settings2 = portage.config(local_config=False)
765     portdb2 = portage.portdbapi(None, settings)
766
767     mask_reason_list = None
768
769     try:
770         for mask_reason_list in portage.getmaskingstatus(package_name, settings2, portdb=portdb2):
771             print_output(debug,portage.output.blue("Mask Reason is " + mask_reason_list))
772             if (mask_reason_list == 'package.mask'):
773                 return True
774     except:
775         return False
776
777     return False
778
779
780
781 # check to see if we have a stable release
782 # in our package.* files that we can remove
783 def check_for_stable_release(pkg):
784     if not is_pkg_package_masked(str(pkg.cpv)):
785         status = _get_mask_status(pkg, True)
786         if status == 0:
787             return True
788     return False
789
790 #print version info
791 def print_version():
792     # Print the version of this tool to the console.
793     print (__productname__ + "(" + __version__ + ") - " + \
794         __description__)
795     print ("Author(s): " + __author__)
796
797 # function to go through a ~cp without a version
798 # and set for removal from file if no masked package version exists
799 def check_tilde_masked_pkg(package_name, filename):
800     ebuild_output=""
801     variable_version = ""
802
803     orig_package_name = package_name
804     package_name = package_name.replace('~','')
805
806     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: orig_package-name is " + orig_package_name))
807     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: package_name is " + package_name))
808
809     query = Query(package_name, True)
810
811     packages = []
812     try:
813         packages = query.smart_find(True,True,True,True,False,True)
814     except errors.GentoolkitException as err:
815         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
816
817     no_versions_installed = True
818     for package in packages:
819         if package.is_installed():
820             no_versions_installed = False
821
822     if (no_versions_installed == True):
823         ebuild_output = portage.output.yellow("\n" + package_name + ": ") + portage.output.red("Not Installed")
824         if "package.unmask" in filename:
825             unmask_list.append(orig_package_name)
826         if "package.keywords" in filename:
827             stable_list.append(orig_package_name)
828         if "package.accept_keywords" in filename:
829             stable_listNg.append(orig_package_name)
830         print (ebuild_output + portage.output.brown(" : " + filename))
831
832         return
833
834     # get all packages matching cat/package
835     if (packages != None):
836         for current_package in packages:
837             print_output(debug,portage.output.blue("check_tilde_maskd_pkg: current_package is " + str(current_package.cpv)))
838             print_output(debug,portage.output.blue("comparing " + package_name + " to " + str(current_package.cpv)))
839             if (pkgcmp(pkgsplit(package_name),pkgsplit(str(current_package.cpv))) <=0 ):
840             #if (pkgcmp(package_name, str(current_package.cpv)) <= 0):
841                 packageObj = gentoolkit.package.Package(str(current_package.cpv))
842                 if (packageObj == None):
843                     # we could not create a package object
844                     return
845
846                 if "package.unmask" in filename:
847                     #if (is_pkg_package_masked(str(current_package.cpv))):
848                     if (is_any_cpv_file_masked(str(current_package.cpv))):
849                         # package was found as masked
850                         return
851                     else:
852                         # package is not masked
853                         unmask_list.append(str(current_package.cpv))
854                         ebuild_output = portage.output.yellow(str(current_package.cpv) + ": ") + portage.output.green("Not package masked")
855                         print_output(info,ebuild_output, package, filename)
856                         return
857                 else:
858                     if (is_pkg_package_masked(str(current_package.cpv))):
859                         # package was found as masked
860                         return 
861         else:
862             # at this point we have no packages >= ~cpv that are masked, present for removal
863             # package does not have any masked versions
864             ebuild_output = portage.output.green(package_name + " has no masked versions")
865             
866             if "package.unmask" in filename:
867                 unmask_list.append(orig_package_name)
868             if "package.keywords" in filename:
869                 stable_list.append(orig_package_name)
870             if "package.accept_keywords" in filename:
871                 stable_listNg.append(orig_package_name)
872             print_output(info,ebuild_output, package, filename)
873
874 #helper function to print avail pks when version does not exist
875 def show_all_versions(pkg, filename):
876
877     # is package masked
878     is_package_masked = False
879     pkgArr = portage.pkgsplit(pkg)
880
881     if pkgArr is None or len(pkgArr) == 0:
882         return
883
884     # determine if category/package is masked 
885     package = pkgArr[0]
886
887     operator = portage.dep.Atom(pkg).operator
888     if operator is not None:
889         package = package[len(operator):]
890
891     # package is category/package and pkg is category/package-version
892     # is category/package-version we are checking package masked?
893     #if portage.settings.pmaskdict.has_key(package):
894     pmaskdict = settings._mask_manager._pmaskdict
895     if package in pmaskdict:
896         pkg_list = pmaskdict.get(package)
897         # iterate through list array looking for pkg
898         for pkg_check in pkg_list:
899             operator = portage.get_operator(pkg_check)
900             if operator is None:
901                 if pkg_check == package:
902                     is_package_masked = True
903
904     query = Query(package)
905
906     all_pkgs = []
907
908     try:
909         all_pkgs = query.smart_find(True,True,True,True,False,True)
910     except errors.GentoolkitException as err:
911         print_output(debug,portage.output.blue("Package " + package + " not found."))
912
913     for current_package in all_pkgs:
914         keywords = "%s" % (current_package.environment("KEYWORDS").split())
915         keywords = filter_keywords(keywords)
916         keywords = "[%s]" % (keywords)
917         ebuild = current_package.ebuild_path()
918         if ebuild:
919             pkgmask = _get_mask_status(current_package, True)
920             if is_package_masked:
921                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
922             elif pkgmask > 4:
923                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
924             elif pkgmask == 4 or pkgmask == 1:
925                 print (portage.output.brown("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
926             else:
927                 print (portage.output.green("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
928                 if "package.keywords" in filename:
929                     stable_list.append(str(current_package.cpv))
930                 if "package.accept_keywords" in filename:
931                     stable_listNg.append(str(current_package.cpv))
932
933
934 def get_useflags(package):
935     iuse = get_iuse(package.cpv)
936     return iuse
937
938 def check_useflags_all_versions(pkgs, line, check_pkgs):
939
940     global useremove_display, invalid_flag_found 
941     invalid_flag_found = False
942    
943     print_output(debug,portage.output.blue("ENTERED check_useflags_all_versions: " + check_pkgs))
944
945     potential_invalid_flag = []
946     valid_flag_list = []
947     for package in pkgs:
948
949         # if package not installed, move on
950         if (not package.is_installed()):
951             continue;
952
953         print_output(debug,portage.output.blue("check_useflags_all_versions: package: " + package.cpv))
954         if ((package is None) or (package == "")):
955             return
956
957         useflag_removal_display=""
958
959         if (len(line) <= 0):
960             return
961
962         list2 = get_useflags(package)
963
964         for uf in list2:
965             print_output(debug,portage.output.blue(("use found " + uf)))
966  
967         iuse_string = package.use()
968         iuse = iuse_string.split(" ")
969
970         for uflag in list2:
971             iuse.append(uflag)
972
973         for iuse_item in iuse:
974             print_output(debug,portage.output.blue(("iuse_item is " + iuse_item)))
975
976         useflags_fromfile = line.replace("\t", " ").split(" ")
977
978         #for useflags_fromfile_item in useflags_fromfile:
979         #    print_output(debug,portage.output.blue(("useflags_fromfile_item is " + useflags_fromfile_item)))
980
981         package_string = useflags_fromfile.pop(0)
982         print_output(debug,portage.output.blue(("package_string is " + package_string)))
983     
984         clean_useflags_list = []
985         # remove + or -
986         atom = "-+"
987     
988         #clean list from portage of + or -
989         clean_iuse = []
990         for item in iuse:
991             if item[0] in atom:
992                 clean_iuse.append(item[1:])
993             else:
994                 clean_iuse.append(item)
995     
996         for original_flag in useflags_fromfile:
997             if (original_flag is None or original_flag == ""):
998                 continue
999             flag = original_flag
1000             if original_flag[0] in atom:
1001                 flag = original_flag[1:]
1002             if flag not in clean_iuse:
1003                 print_output(debug,portage.output.blue(("found invalid flag: " + flag)))
1004
1005                 # only add to invalid list if it's not in valid list
1006                 try:
1007                     index = valid_flag_list.index(original_flag)
1008                 except ValueError as error:
1009
1010                     try:
1011                        index = potential_invalid_flag.index(original_flag)
1012                     except ValueError:
1013                        print_output(debug,portage.output.blue(original_flag + " not found for " + package.cpv))
1014                        potential_invalid_flag.append(original_flag)
1015
1016             else:
1017                 print_output(debug,portage.output.blue(("found valid flag: " + flag)))
1018                 try:
1019                     index = valid_flag_list.index(original_flag)
1020                 except ValueError as error:
1021                     valid_flag_list.append(original_flag)
1022
1023                 try:
1024                     index = potential_invalid_flag.index(original_flag)
1025                     potential_invalid_flag.remove(original_flag)
1026                 except ValueError as error:
1027                     continue
1028
1029
1030     # if potential_invalid_flag list is empty, we are done
1031     if (len(potential_invalid_flag) <= 0):
1032         return
1033
1034     invalid_flag_found = True
1035
1036     # build sentence
1037     invalid_flags = ""
1038     verb = "is"
1039     for inv_flag in potential_invalid_flag:
1040         if (len(invalid_flags) > 0):
1041             invalid_flags += ","
1042             verb = "are"
1043         invalid_flags += inv_flag
1044
1045     # if there are no valid flags at all, we remove the line
1046     if ( len(valid_flag_list) == 0):
1047         print_output (info,portage.output.red("No valid use flags found for package: " + str(package.category) + "/" + str(package.name) + ". Invalid flag(s) found: " + invalid_flags +  "\n"))
1048         useremove_display += "Removing line: " + line + "\n"
1049         use_flag_dict[line] = ""
1050         return
1051
1052     # if there are values in potential_invalid_flag, we need to remove them
1053
1054     if (len(potential_invalid_flag) > 0):
1055         removal_text = "use flags: "
1056     else:
1057         removal_text = "use flag: "
1058
1059     if ( (len(valid_flag_list) > 0)):
1060         useremove_display += "Removing " + removal_text + invalid_flags + " for package " + check_pkgs + "\n"
1061         print (portage.output.yellow(removal_text) +  portage.output.red(invalid_flags) + portage.output.yellow(" " + verb + " invalid for " + str(package.cpv)))
1062
1063     valid_flag_list.insert(0,package_string);
1064
1065     if (len(potential_invalid_flag) > 0):
1066         use_flag_dict[line] = valid_flag_list
1067         
1068     return valid_flag_list
1069
1070 def check_useflags(package,line):
1071
1072     global useremove_display, invalid_flag_found 
1073     invalid_flag_found = False
1074
1075     print_output(debug,portage.output.blue("check_useflags: package: " + package.cpv))
1076     if ((package is None) or (package == "")):
1077         return
1078
1079     useflag_removal_display=""
1080
1081     if (len(line) <= 0):
1082         return
1083
1084     iuse = get_useflags(package)
1085
1086     #for iuse_item in iuse:
1087     #    print_output(debug,portage.output.blue(("iuse_item is " + iuse_item)))
1088
1089     useflags_fromfile = line.replace("\t", " ").split(" ")
1090     #for useflags_fromfile_item in useflags_fromfile:
1091     #    print_output(debug,portage.output.blue(("useflags_fromfile_item is " + useflags_fromfile_item)))
1092
1093     package_string = useflags_fromfile.pop(0)
1094     print_output(debug,portage.output.blue(("package_string is " + package_string)))
1095
1096     clean_useflags_list = []
1097     # remove + or -
1098     atom = "-+"
1099
1100     #clean list from portage of + or -
1101     clean_iuse = []
1102     for item in iuse:
1103         if item[0] in atom:
1104             clean_iuse.append(item[1:])
1105         else:
1106             clean_iuse.append(item)
1107
1108     valid_flag_list = []
1109     for original_flag in useflags_fromfile:
1110         if (original_flag is None or original_flag == ""):
1111             continue
1112         flag = original_flag
1113         if original_flag[0] in atom:
1114             flag = original_flag[1:]
1115         if flag not in clean_iuse:
1116             print_output (info,portage.output.red("use flag: " + flag + " is invalid for : " + str(package.cpv)))
1117             useflag_removal_display += "Removing use flag: " + flag + " for package " + str(package.cpv)
1118             invalid_flag_found = True
1119         else:
1120             valid_flag_list.append(original_flag)
1121
1122     # if valid_flag_list is empty, there are no valid flags
1123     if ( (len(valid_flag_list) > 0) and (len(useflag_removal_display) >0)):
1124         useremove_display += useflag_removal_display + "\n"
1125     elif ( len(valid_flag_list) == 0):
1126         useremove_display += "No valid use flags found for package " + str(package.cpv) + ". Removing line: " + line + "\n"
1127         if ( invalid_flag_found != True):
1128             print_output (info,portage.output.red("No valid use flags found for package " + str(package.cpv)))
1129         invalid_flag_found = True
1130
1131     valid_flag_list.insert(0,package_string);
1132     if (invalid_flag_found == True):
1133         use_flag_dict[line] = valid_flag_list
1134
1135     return valid_flag_list
1136
1137
1138 def clean_useflagsFile(filename):
1139
1140     if "--fix-confirm" in cmdline:
1141         if (confirmFix() == False):
1142             return
1143     
1144     display_line = ""
1145     removed_list = []
1146
1147     try:
1148         # determine if filename is a directory
1149         if os.path.isdir(filename):
1150             # get listing of directory
1151             filenames = os.listdir(filename)
1152             for file_name in filenames:
1153                 clean_useflagsFile(filename+os.path.sep+file_name)
1154             return
1155         else:
1156             #go through stable array and remove line if found
1157             for line in fileinput.input(filename,inplace =1):
1158                 itemFound = False
1159                 line = line.strip()
1160
1161                 # make sure line is not empty and do not remove commented out lines
1162                 if len(line) <= 0:
1163                     continue
1164                 elif line.find("#") == 0:
1165                     print (line)
1166                     continue
1167
1168                 check_for_change = ""
1169                 use_flag_dict.get(line,"")
1170                 try:
1171                     check_for_change = use_flag_dict[line]
1172                     removed_list.append(line)
1173                     if ( len(check_for_change) > 1):
1174                         print (" ".join(check_for_change))
1175                 except KeyError as error:
1176                     print (line)
1177
1178             fileinput.close()
1179     except OSError as error:
1180         print (portage.output.red("Modify/Read access to file: " + filename + " failed: " + format(error)))
1181
1182     if (len(removed_list) > 0):
1183         print ("\n")
1184         for package in removed_list:
1185             print (portage.output.red("Removing from: ") + portage.output.yellow(filename + ": Invalid use flag(s) from ")  + portage.output.green(package) + "\n")
1186
1187     return
1188
1189 def handle_if_overlay(package):
1190     overlay_text = ""
1191     global print_overlay_flag,using_gentoo_as_overlay
1192
1193     if (using_gentoo_as_overlay):
1194         return overlay_text
1195
1196     print_overlay_flag = True
1197
1198     ebuild_path,overlay_path = porttree.dbapi.findname2(str(package.cpv))
1199     index = -1
1200     try:
1201         index = overlay_list.index(overlay_path)
1202     except ValueError as error:
1203         overlay_list.append(overlay_path)
1204         index = overlay_list.index(overlay_path)
1205
1206     overlay_text = " [%s]" % (str(index+1))
1207
1208     return overlay_text
1209
1210 # if the overlay_text was displayed to the user
1211 # we need to display the string at the end 
1212 # this array will store the overlays to be displayed
1213 def print_overlay_text():
1214    
1215     global print_overlay_flag
1216     
1217     if (not print_overlay_flag):
1218         return
1219     
1220     if (len(overlay_list) <= 0):
1221         return
1222
1223     index = 1
1224     for x in overlay_list:
1225         print (portage.output.turquoise("[" + str(index) + "] ") + x)
1226         index = index + 1
1227     
1228     print ("\n")
1229
1230 #helper function to print output
1231 def print_output(log_level,output_string, package=None, filename=None):
1232     
1233     global logLevel
1234
1235     if package != None:
1236         if (package.is_overlay()):
1237             output_string = "%s%s" % (output_string,portage.output.turquoise(handle_if_overlay(package)))
1238             #output_string = output_string + portage.output.turquoise(handle_if_overlay(package))
1239         else:
1240             if filename != None:
1241                 output_string = "%s%s" % (output_string,portage.output.brown(" : " + filename))
1242                 #output_string = output_string + portage.output.brown(" : " + filename)
1243
1244     if (log_level <= logLevel):
1245         print (output_string)
1246
1247 # remove stabled files that are no longer needed from package.keywords
1248 # or package.mask
1249 # includes support for etc/portage/package.keywords/<whatever>/package.keywords
1250 def cleanFile (filename):
1251
1252     removeDups = []
1253     removed_list = []
1254
1255     if "--fix-confirm" in cmdline:
1256         if (confirmFix() == False):
1257             return
1258
1259     # if the file or directory does not exist
1260     # exit out
1261     if (os.path.exists(filename) == False):
1262         return
1263     
1264     if "package.keywords" in filename:
1265         if ( len(stable_list) == 0):
1266             return
1267         removeDups = stable_list
1268     elif "package.accept_keywords" in filename:
1269         if ( len(stable_listNg) == 0):
1270             return
1271         removeDups = stable_listNg
1272     else:
1273         if ( len(unmask_list) == 0):
1274             return
1275         removeDups = unmask_list
1276
1277     removedDict = {}
1278
1279     try:
1280         # determine if filename is a directory
1281         if os.path.isdir(filename):
1282             # get listing of directory
1283             filenames = os.listdir(filename)
1284             for file_name in filenames:
1285                 cleanFile(filename+os.path.sep+file_name)
1286             return
1287         else:   
1288             #go through stable array and remove line if found
1289             for line in fileinput.input(filename,inplace =1):
1290                 itemFound = False
1291                 line = line.strip()
1292
1293                 # make sure line is not empty and do not remove commented out lines
1294                 if len(line) <= 0:
1295                     continue
1296                 elif line.find("#") == 0:
1297                     print (line)
1298                     continue
1299
1300                 for item in removeDups:
1301                     if item in line:
1302                         removed_list.append(item)
1303                         removedDict[filename] = item
1304                         itemFound = True
1305                         removeDups.pop(removeDups.index(item))
1306                         break
1307                 if (itemFound == False):
1308                     print (line)
1309             fileinput.close()
1310     except OSError as error:
1311         print (portage.output.red("Modify/Read access to file: " + filename + " failed: " + format(error)))
1312     
1313     if (len(removed_list) > 0):
1314         print ("\n")
1315         for package in removed_list:
1316             print (portage.output.red("Removing from: ") + portage.output.yellow(filename) + ": " + portage.output.green(package) + "\n")
1317
1318 # ask the user if they want to fix their files
1319 # and remove unneeded entries
1320 # Return true if they say yes and False if they say no
1321 def confirmFix():
1322     
1323     global fix_asked,fix_confirm
1324
1325     if (fix_asked == True):
1326         return fix_confirm
1327
1328     # only ask if we actually have anything to check
1329     if ( (len(stable_list) == 0) and 
1330           (len(stable_listNg) == 0) and
1331           (len(unmask_list) == 0) and
1332           (invalid_flag_found == False) and 
1333           (len(use_flag_dict) == 0)):
1334         fix_confirm = True
1335         return fix_confirm
1336
1337     fix_asked = True 
1338
1339     valid = {"yes":"yes",   "y":"yes", 
1340              "no":"no",     "n":"no"}
1341    
1342     prompt = portage.output.bold("Remove entries from files [") + portage.output.green("y") + "/" + portage.output.red("n") + portage.output.bold("]") + " "
1343
1344     while 1:
1345         sys.stdout.write('\n' + prompt)
1346         choice = input().lower()
1347         if choice == '':
1348             fix_confirm = False
1349             break
1350         elif choice in valid.keys():
1351             if (choice == 'y' or choice == 'yes'):
1352                 fix_confirm = True
1353                 break
1354             else:
1355                 fix_confirm = False
1356                 break 
1357         else:
1358             sys.stdout.write("Please respond with 'yes' or 'no' "\
1359                              "(or 'y' or 'n').\n")
1360
1361     return fix_confirm
1362
1363
1364 def check_for_any_installed_version(pkgs):
1365     
1366     for package in pkgs:
1367         if (package.is_installed()):
1368             return True
1369
1370     return False
1371
1372 # main
1373 if __name__ == "__main__":
1374
1375     if len(sys.argv) == 1:
1376         print_usage()
1377         sys.exit(1)
1378
1379     # soooooo stolen from emerge
1380     tmpcmdline = sys.argv[1:]
1381
1382     for cmd in tmpcmdline:
1383         if cmd[0:1] == "-" and cmd[1:2] != "-":
1384             for cmd_item in cmd[1:]:
1385                 if cmd_item in mappings:
1386                     if mappings[cmd_item] in cmdline:
1387                         print ()
1388                         print ("*** Warning: Redundant use of ", mappings[cmd_item])
1389                     else:
1390                         cmdline.append(mappings[cmd_item])
1391                 else:
1392                     print ("!!! Error: -"+cmd_item+" is an invalid option.")
1393                     sys.exit(-1)
1394         else:
1395             cmdline.append(cmd)
1396
1397     #parse long options
1398     for cmd in cmdline:
1399         if len(cmd)>=2 and cmd[0:2]=="--":
1400             try:
1401                 i = options.index(cmd)
1402                 continue
1403             except ValueError:
1404                 print ("!!! Error: -"+cmd+" is an invalid option.")
1405                 sys.exit(-1)
1406
1407     if "--changes-only" in cmdline:
1408         cmdline.remove("--changes-only")
1409         show_changes_only_flag = True
1410
1411     if "--removable-only" in cmdline:
1412         cmdline.remove("--removable-only")
1413         show_removable_only_flag = True
1414
1415     if "--debug" in cmdline:
1416         logLevel = debug
1417
1418     if "--no-color" in cmdline:
1419         portage.output.nocolor()
1420
1421     if "--tilde-check" in cmdline:
1422         tilde=1
1423
1424     if "--version" in cmdline:
1425         print_version()
1426         sys.exit(0)
1427
1428     if "--fix-confirm" in cmdline:
1429         if '--fix' not in cmdline:
1430             cmdline.append("--fix")
1431     
1432     if "--all" in cmdline:
1433         tmpcmdline = ["--all"]
1434         if "--fix" in cmdline:
1435             tmpcmdline.append("--fix")
1436             if "--fix-confirm" in cmdline:
1437                 tmpcmdline.append("--fix-confirm")
1438                 fix_confirm = False
1439         cmdline=tmpcmdline      
1440
1441     if "--help" in cmdline:
1442         print_usage()
1443         sys.exit(0)
1444     
1445     if (show_changes_only_flag and show_removable_only_flag):
1446         print ("Please select only one of --show-removable (-r) or --changes-only")
1447         print ("Use --help for more info.")
1448         sys.exit(0)
1449         
1450     for cmd in cmdline:
1451         if cmd == "--keyword":
1452             print (portage.output.bold("\npackage.keywords:"))
1453             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
1454             if "--fix" in cmdline:
1455                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
1456             print (portage.output.bold("Done\n"))
1457             print (portage.output.bold("\npackage.accept_keywords:"))
1458             get_recursive_info(USER_CONFIG_PATH + "/package.accept_keywords")
1459             if "--fix" in cmdline:
1460                 cleanFile(USER_CONFIG_PATH + "/package.accept_keywords")
1461             print (portage.output.bold("Done\n"))
1462         elif cmd == "--unmask": 
1463             print (portage.output.bold("\npackage.unmask:"))
1464             checking_package_unmask = True
1465             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
1466             checking_package_unmask = False
1467             if "--fix" in cmdline:
1468                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
1469         elif cmd == "--mask":
1470             checking_package_mask = True
1471             print (portage.output.bold("\npackage.mask:"))
1472             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
1473             print (portage.output.bold("Done\n"))
1474             checking_package_mask = False
1475             if "--fix" in cmdline:
1476                 cleanFile(USER_CONFIG_PATH + "/package.mask")
1477         elif cmd == "--package-use":
1478             print (portage.output.bold("\npackage.use:"))
1479             processing_package_use = True
1480             get_recursive_info(USER_CONFIG_PATH + "/package.use")
1481             if "--fix" in cmdline:
1482                 cleanFile(USER_CONFIG_PATH + "/package.use")
1483                 clean_useflagsFile(USER_CONFIG_PATH + "/package.use")
1484             print (portage.output.bold("Done\n"))
1485         elif cmd == "--all":
1486             print (portage.output.bold("\npackage.keywords:"))
1487             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
1488             print (portage.output.bold("\npackage.accept_keywords:"))
1489             get_recursive_info(USER_CONFIG_PATH + "/package.accept_keywords")
1490             print (portage.output.bold("\npackage.unmask:"))
1491             checking_package_unmask = True
1492             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
1493             checking_package_unmask = False
1494             checking_package_mask = True
1495             print (portage.output.bold("\npackage.mask:"))
1496             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
1497             print (portage.output.bold("\npackage.use:"))
1498             processing_package_use = True
1499             get_recursive_info(USER_CONFIG_PATH + "/package.use")
1500             if "--fix" in cmdline:
1501                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
1502                 cleanFile(USER_CONFIG_PATH + "/package.accept_keywords")
1503                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
1504                 cleanFile(USER_CONFIG_PATH + "/package.use")
1505                 cleanFile(USER_CONFIG_PATH + "/package.mask")
1506                 clean_useflagsFile(USER_CONFIG_PATH + "/package.use")
1507             print (portage.output.bold("\nDone\n"))
1508
1509     print_overlay_text()
1510
1511     if len(cmdline) == 0:
1512         if show_changes_only_flag or show_removable_only_flag:
1513             if (show_changes_only_flag):
1514                 print (portage.output.green("-c") + " or " + portage.output.green("--changes-only") + " must be accompanied by one of the following:")
1515             else:
1516                 print (portage.output.green("-r") + " or " + portage.output.green("--removable-only") + " must be accompanied by one of the following:")
1517
1518             print ("     " + portage.output.yellow("-k") + " or " + portage.output.yellow("--keyword"))
1519             print ("     " + portage.output.yellow("-u") + " or " + portage.output.yellow("--unmask"))
1520             print ("     " + portage.output.yellow("-m") + " or " + portage.output.yellow("--mask"))
1521             print ("     " + portage.output.yellow("-a") + " or " + portage.output.yellow("--all"))
1522
1523             print_usage()
1524