0a4c2b85aa8c7ef23f1bdd684a90ab6586aabf9b
[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.17"
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             package_name = fields[0]
407             if package_name[0] in atom_check:
408                 package_name = package_name[1:]
409             port = Package(package_name)
410             # if package is not installed remove it
411             if ((not port.is_installed())):
412                 print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
413                 unmask_list.append(fields[0])
414
415             show_all_versions(fields[0], filename)
416     current_package = ""
417
418     return diffs_found
419
420 # adding support for etc/portage/package.keywords/<whatever>/package.keywords
421 def get_recursive_info(filename):
422
423     # determine if filename is a directory
424     if os.path.isdir(filename):
425         # get listing of directory
426         filenames = os.listdir(filename)
427         for file_name in filenames:
428             get_recursive_info(filename+os.path.sep+file_name)
429     else:   
430         get_info(filename)
431
432 def get_info(filename):
433
434     diffs_found = False
435     no_file = False
436     filedescriptor = None
437
438     try:
439         filedescriptor = open(filename)
440         for line in filedescriptor.readlines():
441             line = line.strip()
442             if len(line) <= 0:
443                 continue
444             elif line.find("#") >= 0:
445                 # found '#' remove comment
446                 if (skipFile(line,filename)):
447                     return
448                 line = line[0:line.find("#")]
449                 line = line.strip()
450                 if len(line) <= 0:
451                     continue
452             if (processing_package_use == False):
453                 diffs_found = parse_line(line, filename)
454             else:
455                 # process package.use
456                 diffs_found = parse_package_use(line,filename)
457     except IOError:
458         print (portage.output.red("Could not find file " + filename))
459         no_file = True
460
461     if not diffs_found and no_file:
462         print (portage.output.brown("No ebuild options found."))
463
464     # close file
465     if (filedescriptor != None):
466         filedescriptor.close()
467
468 # parse the package.use file and look for packages
469 # not installed
470 def parse_package_use(line, filename):
471
472     global info,debug
473     print_output(debug,portage.output.blue("parse_package_use: Line: " + line))
474     pkgs = None
475     check_pkg = ""
476     pkg_length = 0
477     atom_check="<>="
478     any_version = False
479     has_atom = True
480
481     diffs_found = False
482     package_installed = False
483     fields = line.replace("\t", " ").split(" ")
484     less_than = False
485     less_than_or_equal = False
486
487     if len(fields) > 0:
488         check_pkg = fields[0] # this could be one of <>=
489         orig_pkg_name = check_pkg
490         if check_pkg[0] not in atom_check:
491             has_atom = False
492         else:
493             if check_pkg[0] == '<':
494                 less_than = True;
495                 if check_pkg[1] == '=':
496                     less_than_or_equal = True
497
498             check_pkg = check_pkg[1:]
499             if check_pkg[0] in atom_check:
500                 check_pkg = check_pkg[1:]
501
502
503         # if there is a wildcard, check to make sure at least one
504         # of the packages is installed
505         if check_pkg.find("/*") >= 0:
506             query = Query(check_pkg)
507             pkgs = []
508             try:
509                 pkgs = query.smart_find(True,True,True,True,False,True)
510             except errors.GentoolkitException as err:
511                 print_output(debug,portage.output.blue("parse_package_use: Package " + check_pkg + " not found."))
512                 return False
513
514             if (pkgs != None):
515                 pkg_length = len(pkgs)
516             for current_package in pkgs:
517                 # on wildcard scenario, return False if one package is installed
518                 if current_package.is_installed():
519                     check_useflags(current_package,line)
520                     return False
521         check_pkg = (check_pkg.rsplit(':',1))[0]
522         
523         if ((orig_pkg_name.find("<=") >=0) or (orig_pkg_name.find("<")>=0) or (orig_pkg_name.find(">=") >=0) or (orig_pkg_name.find(">") >=0)):
524             query = Query(check_pkg)
525             pkgs = []
526             try:
527                 pkgs = query.smart_find(True,True,True,True,False,True)
528             except errors.GentoolkitException as err:
529                 print_output(debug,portage.output.blue("parse_package_use: Package " + check_pkg + " not found."))
530                 return False
531
532             if (pkgs != None):
533                 pkg_length = len(pkgs)
534             for current_package in pkgs:
535                 # on wildcard scenario, return False if one package is installed
536                 if (orig_pkg_name.find("<=") >=0):
537                     if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) <= 0):
538                         if current_package.is_installed():
539                             check_useflags(current_package,line)
540                         return False
541                 else:
542                     if (orig_pkg_name.find("<") >=0):
543                         if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) < 0):
544                             if current_package.is_installed():
545                                 check_useflags(current_package,line)
546                             return False
547                 if (orig_pkg_name.find(">=") >=0):
548                     if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) == 0) or (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) == 2):
549                         if current_package.is_installed():
550                             check_useflags(current_package,line)
551                         return False
552                 else:
553                     if (orig_pkg_name.find(">") >=0):
554                         if (pkgcmp(pkgsplit(check_pkg),pkgsplit(str(current_package.cpv))) == 2):
555                             if current_package.is_installed():
556                                 check_useflags(current_package,line)
557                             return False
558         else:
559             # look for any version of check_pkg installed as there is 
560             # no version specified in package.use
561             package_exists = portage.portdb.xmatch("match-all", check_pkg)
562             if package_exists:
563                 # get all package versions
564                 query = Query(check_pkg)
565                 pkgs = []
566                 try:
567                     pkgs = query.smart_find(True,True,True,True,False,True)
568                 except errors.GentoolkitException as err:
569                     print_output(debug,portage.output.blue("Package " + check_pkg + " not found."))
570     
571                 if (pkgs != None):
572                     pkg_length = len(pkgs)
573     
574                     # go through each package version for a specific 
575                     # version found above
576                     # if one line check returns all ok, we don't need 
577                     #to check the rest. One slot could have different 
578                     #use flags than another
579                     if (has_atom == False):
580                         check_useflags_all_versions(pkgs, line, check_pkg)
581                         package_installed = check_for_any_installed_version(pkgs)
582                         current_package = None
583
584                         #for current_package in pkgs:
585                         #    if current_package.is_installed():
586                         #        check_useflags(current_package,line)
587                         #        package_installed = True
588                         #        if (invalid_flag_found == False):
589                         #            break;
590                     else: 
591                         # go through each package version for a specific version found above
592                         for current_package in pkgs:
593                             if (str(current_package.cpv) == check_pkg):
594                                 if not current_package.is_installed():
595                                     print_output(info,portage.output.yellow("\n" + orig_pkg_name + ": ") + portage.output.red("Not Installed"),current_package)
596                                     if "package.keywords" in filename:
597                                         stable_list.append(check_pkg)
598                                     if "package.accept_keywords" in filename:
599                                         stable_listNg.append(check_pkg)
600                                     unmask_list.append(check_pkg)
601                                     if "package.use" in filename:
602                                         valid_flag_list = []
603                                         valid_flag_list.insert(0,current_package);
604                                         use_flag_dict[line] = valid_flag_list
605                                         check_for_change = use_flag_dict[line]
606                                     return True
607                                 else:
608                                     check_useflags(current_package,line)
609                                     return False
610             else:
611                 print (portage.output.red ("\nPackage: " + fields[0] + " not found. Please check " + filename + " to validate entry"))
612                 if "package.keywords" in filename:
613                     stable_list.append(check_pkg)
614                 if "package.accept_keywords" in filename:
615                     stable_listNg.append(check_pkg)
616                 unmask_list.append(check_pkg)
617                 if "package.use" in filename:
618                     valid_flag_list = []
619                     #valid_flag_list.insert(0,current_package);
620                     valid_flag_list.insert(0,fields[0]);
621                     use_flag_dict[line] = valid_flag_list
622                     check_for_change = use_flag_dict[line]
623                 return True
624
625     if (package_installed == False):
626         # package does not exists
627         print_output(info,portage.output.yellow("\n" + orig_pkg_name + ": ") + portage.output.red(" Not Installed"),current_package)
628         if "package.keywords" in filename:
629             stable_list.append(check_pkg)
630         if "package.accept_keywords" in filename:
631             stable_listNg.append(check_pkg)
632         if "package.use" in filename:
633             valid_flag_list = []
634             valid_flag_list.insert(0,current_package);
635             use_flag_dict[line] = valid_flag_list
636             check_for_change = use_flag_dict[line]
637
638         unmask_list.append(check_pkg)
639         return True
640
641     return False
642
643
644
645
646 # skip the file if portpeek-skip found in a comment
647 # you can put this in the middle of a file and it will
648 #skip all entrie below it, this is useful to speed things
649 #up by not checking ~kde versions, for example
650 def skipFile (line, filename):
651     if ( (line == None) or (filename == None)):
652         return False
653
654     if line.find("portpeek-skip") >= 0:
655         return True
656
657     return False
658
659 # parts blatantly stolen from equery
660 # if pure is true, then get "true" mask status that is
661 # not affected by entries in /etc/portage/package.*
662 def _get_mask_status(pkg, pure):
663     pkgmask = 0
664
665     if (pkg == None):
666         return 0
667
668     if pkg.is_masked():
669         pkgmask = pkgmask + 3
670
671     if pure:
672         try:
673             keywords = portage.portdb.aux_get(str(pkg.cpv), ["KEYWORDS"])
674             keywords = keywords[0].split()
675         except KeyError:
676             # cpv does not exist
677             return 0
678     else:
679         keywords = pkg.environment("KEYWORDS").split()
680
681     # first check for stable arch, stop there if it is found
682     if settings["ARCH"] in keywords:
683         return 0
684
685     if "~" + settings["ARCH"] in keywords:
686         pkgmask = pkgmask + 1
687     elif "-*" in keywords or "-" + settings["ARCH"] in keywords:
688         pkgmask = pkgmask + 2
689     
690     return pkgmask
691
692 def is_pkg_package_masked(cpv):
693
694     print_output(debug,portage.output.blue("is_pkg_package_masked called: " + cpv))
695     settings2 = portage.config(local_config=False)
696     portdb2 = portage.portdbapi(None, settings)
697
698     mask_reason_list = None
699     
700     try:
701         for mask_reason_list in portage.getmaskingstatus(cpv, settings2, portdb=portdb2):
702             return True
703     except:
704         return False
705
706     return False
707
708 # filter out keywords for archs other than the current one
709 def filter_keywords(keywords):
710     filtered_keywords = ""
711
712     #for key in key_list:
713     for key in keywords:
714         key = key.replace("[", '')
715         key = key.replace("]", "")
716         key = key.replace(",", "")
717         arch=settings["ARCH"]
718
719         # remove '~' from key for comparison
720         key_comparison = key.lstrip('~')
721         if key_comparison == arch:
722             if len(filtered_keywords) != 0:
723                 filtered_keywords = filtered_keywords + " "
724             filtered_keywords = filtered_keywords + key
725         elif "-*" in key:
726             if len(filtered_keywords) != 0:
727                 filtered_keywords = filtered_keywords + " "
728             filtered_keywords = filtered_keywords + key
729
730
731     return filtered_keywords
732
733
734 # this function takes in a package with no cpv
735 # and returns True if any version is masked
736 def is_any_cpv_keyword_masked(package_name):
737     print_output(debug,portage.output.blue("inside is_any_cpv_keyword_masked: " + package_name))
738
739     query = Query(package_name, True)
740
741     packages = []
742     try:
743         packages = query.smart_find(True,True,True,True,False,True)
744     except errors.GentoolkitException as err:
745         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
746
747     for package in packages:
748         if is_pkg_package_masked(package.cpv):
749             print_output(debug,portage.output.blue("Package " + package.cpv + " is masked."))
750             return True
751
752     print_output(debug,portage.output.blue("Package " + package_name + ". No masked versions."))
753     return False
754
755 # we need to know if the package is masked
756 # by being present in the package.mask file
757 # as opposed to masked by keyword.
758 # if it's not, then we can remove from package.unmask
759 def is_any_cpv_file_masked(package_name):
760
761     print_output(debug,portage.output.blue("is_any_cpv_file_masked called: " + package_name))
762     settings2 = portage.config(local_config=False)
763     portdb2 = portage.portdbapi(None, settings)
764
765     mask_reason_list = None
766
767     try:
768         for mask_reason_list in portage.getmaskingstatus(package_name, settings2, portdb=portdb2):
769             print_output(debug,portage.output.blue("Mask Reason is " + mask_reason_list))
770             if (mask_reason_list == 'package.mask'):
771                 return True
772     except:
773         return False
774
775     return False
776
777
778
779 # check to see if we have a stable release
780 # in our package.* files that we can remove
781 def check_for_stable_release(pkg):
782     if not is_pkg_package_masked(str(pkg.cpv)):
783         status = _get_mask_status(pkg, True)
784         if status == 0:
785             return True
786     return False
787
788 #print version info
789 def print_version():
790     # Print the version of this tool to the console.
791     print (__productname__ + "(" + __version__ + ") - " + \
792         __description__)
793     print ("Author(s): " + __author__)
794
795 # function to go through a ~cp without a version
796 # and set for removal from file if no masked package version exists
797 def check_tilde_masked_pkg(package_name, filename):
798     ebuild_output=""
799     variable_version = ""
800
801     orig_package_name = package_name
802     package_name = package_name.replace('~','')
803
804     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: orig_package-name is " + orig_package_name))
805     print_output(debug,portage.output.blue("check_tilde_maskd_pkg: package_name is " + package_name))
806
807     query = Query(package_name, True)
808
809     packages = []
810     try:
811         packages = query.smart_find(True,True,True,True,False,True)
812     except errors.GentoolkitException as err:
813         print_output(debug,portage.output.blue("Package " + package_name + " not found."))
814
815     no_versions_installed = True
816     for package in packages:
817         if package.is_installed():
818             no_versions_installed = False
819
820     if (no_versions_installed == True):
821         ebuild_output = portage.output.yellow("\n" + package_name + ": ") + portage.output.red("Not Installed")
822         if "package.unmask" in filename:
823             unmask_list.append(orig_package_name)
824         if "package.keywords" in filename:
825             stable_list.append(orig_package_name)
826         if "package.accept_keywords" in filename:
827             stable_listNg.append(orig_package_name)
828         print (ebuild_output + portage.output.brown(" : " + filename))
829
830         return
831
832     # get all packages matching cat/package
833     if (packages != None):
834         for current_package in packages:
835             print_output(debug,portage.output.blue("check_tilde_maskd_pkg: current_package is " + str(current_package.cpv)))
836             print_output(debug,portage.output.blue("comparing " + package_name + " to " + str(current_package.cpv)))
837             if (pkgcmp(pkgsplit(package_name),pkgsplit(str(current_package.cpv))) <=0 ):
838             #if (pkgcmp(package_name, str(current_package.cpv)) <= 0):
839                 packageObj = gentoolkit.package.Package(str(current_package.cpv))
840                 if (packageObj == None):
841                     # we could not create a package object
842                     return
843
844                 if "package.unmask" in filename:
845                     #if (is_pkg_package_masked(str(current_package.cpv))):
846                     if (is_any_cpv_file_masked(str(current_package.cpv))):
847                         # package was found as masked
848                         return
849                     else:
850                         # package is not masked
851                         unmask_list.append(str(current_package.cpv))
852                         ebuild_output = portage.output.yellow(str(current_package.cpv) + ": ") + portage.output.green("Not package masked")
853                         print_output(info,ebuild_output, package, filename)
854                         return
855                 else:
856                     if (is_pkg_package_masked(str(current_package.cpv))):
857                         # package was found as masked
858                         return 
859         else:
860             # at this point we have no packages >= ~cpv that are masked, present for removal
861             # package does not have any masked versions
862             ebuild_output = portage.output.green(package_name + " has no masked versions")
863             
864             if "package.unmask" in filename:
865                 unmask_list.append(orig_package_name)
866             if "package.keywords" in filename:
867                 stable_list.append(orig_package_name)
868             if "package.accept_keywords" in filename:
869                 stable_listNg.append(orig_package_name)
870             print_output(info,ebuild_output, package, filename)
871
872 #helper function to print avail pks when version does not exist
873 def show_all_versions(pkg, filename):
874
875     # is package masked
876     is_package_masked = False
877     pkgArr = portage.pkgsplit(pkg)
878
879     if pkgArr is None or len(pkgArr) == 0:
880         return
881
882     # determine if category/package is masked 
883     package = pkgArr[0]
884
885     operator = portage.dep.Atom(pkg).operator
886     if operator is not None:
887         package = package[len(operator):]
888
889     # package is category/package and pkg is category/package-version
890     # is category/package-version we are checking package masked?
891     #if portage.settings.pmaskdict.has_key(package):
892     pmaskdict = settings._mask_manager._pmaskdict
893     if package in pmaskdict:
894         pkg_list = pmaskdict.get(package)
895         # iterate through list array looking for pkg
896         for pkg_check in pkg_list:
897             operator = portage.get_operator(pkg_check)
898             if operator is None:
899                 if pkg_check == package:
900                     is_package_masked = True
901
902     query = Query(package)
903
904     all_pkgs = []
905
906     try:
907         all_pkgs = query.smart_find(True,True,True,True,False,True)
908     except errors.GentoolkitException as err:
909         print_output(debug,portage.output.blue("Package " + package + " not found."))
910
911     for current_package in all_pkgs:
912         keywords = "%s" % (current_package.environment("KEYWORDS").split())
913         keywords = filter_keywords(keywords)
914         keywords = "[%s]" % (keywords)
915         ebuild = current_package.ebuild_path()
916         if ebuild:
917             pkgmask = _get_mask_status(current_package, True)
918             if is_package_masked:
919                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
920             elif pkgmask > 4:
921                 print (portage.output.red("Available: " + str(current_package.cpv) + " [M] Keywords: " + keywords))
922             elif pkgmask == 4 or pkgmask == 1:
923                 print (portage.output.brown("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
924             else:
925                 print (portage.output.green("Available: " + str(current_package.cpv) + " Keywords: " + keywords))
926                 if "package.keywords" in filename:
927                     stable_list.append(str(current_package.cpv))
928                 if "package.accept_keywords" in filename:
929                     stable_listNg.append(str(current_package.cpv))
930
931
932 def get_useflags(package):
933     iuse = get_iuse(package.cpv)
934     return iuse
935
936 def check_useflags_all_versions(pkgs, line, check_pkgs):
937
938     global useremove_display, invalid_flag_found 
939     invalid_flag_found = False
940    
941     print_output(debug,portage.output.blue("ENTERED check_useflags_all_versions: " + check_pkgs))
942
943     potential_invalid_flag = []
944     valid_flag_list = []
945     for package in pkgs:
946
947         # if package not installed, move on
948         if (not package.is_installed()):
949             continue;
950
951         print_output(debug,portage.output.blue("check_useflags_all_versions: package: " + package.cpv))
952         if ((package is None) or (package == "")):
953             return
954
955         useflag_removal_display=""
956
957         if (len(line) <= 0):
958             return
959
960         list2 = get_useflags(package)
961
962         for uf in list2:
963             print_output(debug,portage.output.blue(("use found " + uf)))
964  
965         iuse_string = package.use()
966         iuse = iuse_string.split(" ")
967
968         for uflag in list2:
969             iuse.append(uflag)
970
971         for iuse_item in iuse:
972             print_output(debug,portage.output.blue(("iuse_item is " + iuse_item)))
973
974         useflags_fromfile = line.replace("\t", " ").split(" ")
975
976         #for useflags_fromfile_item in useflags_fromfile:
977         #    print_output(debug,portage.output.blue(("useflags_fromfile_item is " + useflags_fromfile_item)))
978
979         package_string = useflags_fromfile.pop(0)
980         print_output(debug,portage.output.blue(("package_string is " + package_string)))
981     
982         clean_useflags_list = []
983         # remove + or -
984         atom = "-+"
985     
986         #clean list from portage of + or -
987         clean_iuse = []
988         for item in iuse:
989             if item[0] in atom:
990                 clean_iuse.append(item[1:])
991             else:
992                 clean_iuse.append(item)
993     
994         for original_flag in useflags_fromfile:
995             if (original_flag is None or original_flag == ""):
996                 continue
997             flag = original_flag
998             if original_flag[0] in atom:
999                 flag = original_flag[1:]
1000             if flag not in clean_iuse:
1001                 print_output(debug,portage.output.blue(("found invalid flag: " + flag)))
1002
1003                 # only add to invalid list if it's not in valid list
1004                 try:
1005                     index = valid_flag_list.index(original_flag)
1006                 except ValueError as error:
1007
1008                     try:
1009                        index = potential_invalid_flag.index(original_flag)
1010                     except ValueError:
1011                        print_output(debug,portage.output.blue(original_flag + " not found for " + package.cpv))
1012                        potential_invalid_flag.append(original_flag)
1013
1014             else:
1015                 print_output(debug,portage.output.blue(("found valid flag: " + flag)))
1016                 try:
1017                     index = valid_flag_list.index(original_flag)
1018                 except ValueError as error:
1019                     valid_flag_list.append(original_flag)
1020
1021                 try:
1022                     index = potential_invalid_flag.index(original_flag)
1023                     potential_invalid_flag.remove(original_flag)
1024                 except ValueError as error:
1025                     continue
1026
1027
1028     # if potential_invalid_flag list is empty, we are done
1029     if (len(potential_invalid_flag) <= 0):
1030         return
1031
1032     invalid_flag_found = True
1033
1034     # build sentence
1035     invalid_flags = ""
1036     verb = "is"
1037     for inv_flag in potential_invalid_flag:
1038         if (len(invalid_flags) > 0):
1039             invalid_flags += ","
1040             verb = "are"
1041         invalid_flags += inv_flag
1042
1043     # if there are no valid flags at all, we remove the line
1044     if ( len(valid_flag_list) == 0):
1045         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"))
1046         useremove_display += "Removing line: " + line + "\n"
1047         use_flag_dict[line] = ""
1048         return
1049
1050     # if there are values in potential_invalid_flag, we need to remove them
1051
1052     if (len(potential_invalid_flag) > 0):
1053         removal_text = "use flags: "
1054     else:
1055         removal_text = "use flag: "
1056
1057     if ( (len(valid_flag_list) > 0)):
1058         useremove_display += "Removing " + removal_text + invalid_flags + " for package " + check_pkgs + "\n"
1059         print (portage.output.yellow(removal_text) +  portage.output.red(invalid_flags) + portage.output.yellow(" " + verb + " invalid for " + str(package.cpv)))
1060
1061     valid_flag_list.insert(0,package_string);
1062
1063     if (len(potential_invalid_flag) > 0):
1064         use_flag_dict[line] = valid_flag_list
1065         
1066     return valid_flag_list
1067
1068 def check_useflags(package,line):
1069
1070     global useremove_display, invalid_flag_found 
1071     invalid_flag_found = False
1072
1073     print_output(debug,portage.output.blue("check_useflags: package: " + package.cpv))
1074     if ((package is None) or (package == "")):
1075         return
1076
1077     useflag_removal_display=""
1078
1079     if (len(line) <= 0):
1080         return
1081
1082     iuse = get_useflags(package)
1083
1084     #for iuse_item in iuse:
1085     #    print_output(debug,portage.output.blue(("iuse_item is " + iuse_item)))
1086
1087     useflags_fromfile = line.replace("\t", " ").split(" ")
1088     #for useflags_fromfile_item in useflags_fromfile:
1089     #    print_output(debug,portage.output.blue(("useflags_fromfile_item is " + useflags_fromfile_item)))
1090
1091     package_string = useflags_fromfile.pop(0)
1092     print_output(debug,portage.output.blue(("package_string is " + package_string)))
1093
1094     clean_useflags_list = []
1095     # remove + or -
1096     atom = "-+"
1097
1098     #clean list from portage of + or -
1099     clean_iuse = []
1100     for item in iuse:
1101         if item[0] in atom:
1102             clean_iuse.append(item[1:])
1103         else:
1104             clean_iuse.append(item)
1105
1106     valid_flag_list = []
1107     for original_flag in useflags_fromfile:
1108         if (original_flag is None or original_flag == ""):
1109             continue
1110         flag = original_flag
1111         if original_flag[0] in atom:
1112             flag = original_flag[1:]
1113         if flag not in clean_iuse:
1114             print_output (info,portage.output.red("use flag: " + flag + " is invalid for : " + str(package.cpv)))
1115             useflag_removal_display += "Removing use flag: " + flag + " for package " + str(package.cpv)
1116             invalid_flag_found = True
1117         else:
1118             valid_flag_list.append(original_flag)
1119
1120     # if valid_flag_list is empty, there are no valid flags
1121     if ( (len(valid_flag_list) > 0) and (len(useflag_removal_display) >0)):
1122         useremove_display += useflag_removal_display + "\n"
1123     elif ( len(valid_flag_list) == 0):
1124         useremove_display += "No valid use flags found for package " + str(package.cpv) + ". Removing line: " + line + "\n"
1125         if ( invalid_flag_found != True):
1126             print_output (info,portage.output.red("No valid use flags found for package " + str(package.cpv)))
1127         invalid_flag_found = True
1128
1129     valid_flag_list.insert(0,package_string);
1130     if (invalid_flag_found == True):
1131         use_flag_dict[line] = valid_flag_list
1132
1133     return valid_flag_list
1134
1135
1136 def clean_useflagsFile(filename):
1137
1138     if "--fix-confirm" in cmdline:
1139         if (confirmFix() == False):
1140             return
1141     
1142     display_line = ""
1143     removed_list = []
1144
1145     try:
1146         # determine if filename is a directory
1147         if os.path.isdir(filename):
1148             # get listing of directory
1149             filenames = os.listdir(filename)
1150             for file_name in filenames:
1151                 clean_useflagsFile(filename+os.path.sep+file_name)
1152             return
1153         else:
1154             #go through stable array and remove line if found
1155             for line in fileinput.input(filename,inplace =1):
1156                 itemFound = False
1157                 line = line.strip()
1158
1159                 # make sure line is not empty and do not remove commented out lines
1160                 if len(line) <= 0:
1161                     continue
1162                 elif line.find("#") == 0:
1163                     print (line)
1164                     continue
1165
1166                 check_for_change = ""
1167                 use_flag_dict.get(line,"")
1168                 try:
1169                     check_for_change = use_flag_dict[line]
1170                     removed_list.append(line)
1171                     if ( len(check_for_change) > 1):
1172                         print (" ".join(check_for_change))
1173                 except KeyError as error:
1174                     print (line)
1175
1176             fileinput.close()
1177     except OSError as error:
1178         print (portage.output.red("Modify/Read access to file: " + filename + " failed: " + format(error)))
1179
1180     if (len(removed_list) > 0):
1181         print ("\n")
1182         for package in removed_list:
1183             print (portage.output.red("Removing from: ") + portage.output.yellow(filename + ": Invalid use flag(s) from ")  + portage.output.green(package) + "\n")
1184
1185     return
1186
1187 def handle_if_overlay(package):
1188     overlay_text = ""
1189     global print_overlay_flag,using_gentoo_as_overlay
1190
1191     if (using_gentoo_as_overlay):
1192         return overlay_text
1193
1194     print_overlay_flag = True
1195
1196     ebuild_path,overlay_path = porttree.dbapi.findname2(str(package.cpv))
1197     index = -1
1198     try:
1199         index = overlay_list.index(overlay_path)
1200     except ValueError as error:
1201         overlay_list.append(overlay_path)
1202         index = overlay_list.index(overlay_path)
1203
1204     overlay_text = " [%s]" % (str(index+1))
1205
1206     return overlay_text
1207
1208 # if the overlay_text was displayed to the user
1209 # we need to display the string at the end 
1210 # this array will store the overlays to be displayed
1211 def print_overlay_text():
1212    
1213     global print_overlay_flag
1214     
1215     if (not print_overlay_flag):
1216         return
1217     
1218     if (len(overlay_list) <= 0):
1219         return
1220
1221     index = 1
1222     for x in overlay_list:
1223         print (portage.output.turquoise("[" + str(index) + "] ") + x)
1224         index = index + 1
1225     
1226     print ("\n")
1227
1228 #helper function to print output
1229 def print_output(log_level,output_string, package=None, filename=None):
1230     
1231     global logLevel
1232
1233     if package != None:
1234         if (package.is_overlay()):
1235             output_string = "%s%s" % (output_string,portage.output.turquoise(handle_if_overlay(package)))
1236             #output_string = output_string + portage.output.turquoise(handle_if_overlay(package))
1237         else:
1238             if filename != None:
1239                 output_string = "%s%s" % (output_string,portage.output.brown(" : " + filename))
1240                 #output_string = output_string + portage.output.brown(" : " + filename)
1241
1242     if (log_level <= logLevel):
1243         print (output_string)
1244
1245 # remove stabled files that are no longer needed from package.keywords
1246 # or package.mask
1247 # includes support for etc/portage/package.keywords/<whatever>/package.keywords
1248 def cleanFile (filename):
1249
1250     removeDups = []
1251     removed_list = []
1252
1253     if "--fix-confirm" in cmdline:
1254         if (confirmFix() == False):
1255             return
1256
1257     # if the file or directory does not exist
1258     # exit out
1259     if (os.path.exists(filename) == False):
1260         return
1261     
1262     if "package.keywords" in filename:
1263         if ( len(stable_list) == 0):
1264             return
1265         removeDups = stable_list
1266     elif "package.accept_keywords" in filename:
1267         if ( len(stable_listNg) == 0):
1268             return
1269         removeDups = stable_listNg
1270     else:
1271         if ( len(unmask_list) == 0):
1272             return
1273         removeDups = unmask_list
1274
1275     removedDict = {}
1276
1277     try:
1278         # determine if filename is a directory
1279         if os.path.isdir(filename):
1280             # get listing of directory
1281             filenames = os.listdir(filename)
1282             for file_name in filenames:
1283                 cleanFile(filename+os.path.sep+file_name)
1284             return
1285         else:   
1286             #go through stable array and remove line if found
1287             for line in fileinput.input(filename,inplace =1):
1288                 itemFound = False
1289                 line = line.strip()
1290
1291                 # make sure line is not empty and do not remove commented out lines
1292                 if len(line) <= 0:
1293                     continue
1294                 elif line.find("#") == 0:
1295                     print (line)
1296                     continue
1297
1298                 for item in removeDups:
1299                     if item in line:
1300                         removed_list.append(item)
1301                         removedDict[filename] = item
1302                         itemFound = True
1303                         removeDups.pop(removeDups.index(item))
1304                         break
1305                 if (itemFound == False):
1306                     print (line)
1307             fileinput.close()
1308     except OSError as error:
1309         print (portage.output.red("Modify/Read access to file: " + filename + " failed: " + format(error)))
1310     
1311     if (len(removed_list) > 0):
1312         print ("\n")
1313         for package in removed_list:
1314             print (portage.output.red("Removing from: ") + portage.output.yellow(filename) + ": " + portage.output.green(package) + "\n")
1315
1316 # ask the user if they want to fix their files
1317 # and remove unneeded entries
1318 # Return true if they say yes and False if they say no
1319 def confirmFix():
1320     
1321     global fix_asked,fix_confirm
1322
1323     if (fix_asked == True):
1324         return fix_confirm
1325
1326     # only ask if we actually have anything to check
1327     if ( (len(stable_list) == 0) and 
1328           (len(stable_listNg) == 0) and
1329           (len(unmask_list) == 0) and
1330           (invalid_flag_found == False) and 
1331           (len(use_flag_dict) == 0)):
1332         fix_confirm = True
1333         return fix_confirm
1334
1335     fix_asked = True 
1336
1337     valid = {"yes":"yes",   "y":"yes", 
1338              "no":"no",     "n":"no"}
1339    
1340     prompt = portage.output.bold("Remove entries from files [") + portage.output.green("y") + "/" + portage.output.red("n") + portage.output.bold("]") + " "
1341
1342     while 1:
1343         sys.stdout.write('\n' + prompt)
1344         choice = input().lower()
1345         if choice == '':
1346             fix_confirm = False
1347             break
1348         elif choice in valid.keys():
1349             if (choice == 'y' or choice == 'yes'):
1350                 fix_confirm = True
1351                 break
1352             else:
1353                 fix_confirm = False
1354                 break 
1355         else:
1356             sys.stdout.write("Please respond with 'yes' or 'no' "\
1357                              "(or 'y' or 'n').\n")
1358
1359     return fix_confirm
1360
1361
1362 def check_for_any_installed_version(pkgs):
1363     
1364     for package in pkgs:
1365         if (package.is_installed()):
1366             return True
1367
1368     return False
1369
1370 # main
1371 if __name__ == "__main__":
1372
1373     if len(sys.argv) == 1:
1374         print_usage()
1375         sys.exit(1)
1376
1377     # soooooo stolen from emerge
1378     tmpcmdline = sys.argv[1:]
1379
1380     for cmd in tmpcmdline:
1381         if cmd[0:1] == "-" and cmd[1:2] != "-":
1382             for cmd_item in cmd[1:]:
1383                 if cmd_item in mappings:
1384                     if mappings[cmd_item] in cmdline:
1385                         print ()
1386                         print ("*** Warning: Redundant use of ", mappings[cmd_item])
1387                     else:
1388                         cmdline.append(mappings[cmd_item])
1389                 else:
1390                     print ("!!! Error: -"+cmd_item+" is an invalid option.")
1391                     sys.exit(-1)
1392         else:
1393             cmdline.append(cmd)
1394
1395     #parse long options
1396     for cmd in cmdline:
1397         if len(cmd)>=2 and cmd[0:2]=="--":
1398             try:
1399                 i = options.index(cmd)
1400                 continue
1401             except ValueError:
1402                 print ("!!! Error: -"+cmd+" is an invalid option.")
1403                 sys.exit(-1)
1404
1405     if "--changes-only" in cmdline:
1406         cmdline.remove("--changes-only")
1407         show_changes_only_flag = True
1408
1409     if "--removable-only" in cmdline:
1410         cmdline.remove("--removable-only")
1411         show_removable_only_flag = True
1412
1413     if "--debug" in cmdline:
1414         logLevel = debug
1415
1416     if "--no-color" in cmdline:
1417         portage.output.nocolor()
1418
1419     if "--tilde-check" in cmdline:
1420         tilde=1
1421
1422     if "--version" in cmdline:
1423         print_version()
1424         sys.exit(0)
1425
1426     if "--fix-confirm" in cmdline:
1427         if '--fix' not in cmdline:
1428             cmdline.append("--fix")
1429     
1430     if "--all" in cmdline:
1431         tmpcmdline = ["--all"]
1432         if "--fix" in cmdline:
1433             tmpcmdline.append("--fix")
1434             if "--fix-confirm" in cmdline:
1435                 tmpcmdline.append("--fix-confirm")
1436                 fix_confirm = False
1437         cmdline=tmpcmdline      
1438
1439     if "--help" in cmdline:
1440         print_usage()
1441         sys.exit(0)
1442     
1443     if (show_changes_only_flag and show_removable_only_flag):
1444         print ("Please select only one of --show-removable (-r) or --changes-only")
1445         print ("Use --help for more info.")
1446         sys.exit(0)
1447         
1448     for cmd in cmdline:
1449         if cmd == "--keyword":
1450             print (portage.output.bold("\npackage.keywords:"))
1451             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
1452             if "--fix" in cmdline:
1453                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
1454             print (portage.output.bold("Done\n"))
1455             print (portage.output.bold("\npackage.accept_keywords:"))
1456             get_recursive_info(USER_CONFIG_PATH + "/package.accept_keywords")
1457             if "--fix" in cmdline:
1458                 cleanFile(USER_CONFIG_PATH + "/package.accept_keywords")
1459             print (portage.output.bold("Done\n"))
1460         elif cmd == "--unmask": 
1461             print (portage.output.bold("\npackage.unmask:"))
1462             checking_package_unmask = True
1463             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
1464             checking_package_unmask = False
1465             if "--fix" in cmdline:
1466                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
1467         elif cmd == "--mask":
1468             checking_package_mask = True
1469             print (portage.output.bold("\npackage.mask:"))
1470             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
1471             print (portage.output.bold("Done\n"))
1472             checking_package_mask = False
1473             if "--fix" in cmdline:
1474                 cleanFile(USER_CONFIG_PATH + "/package.mask")
1475         elif cmd == "--package-use":
1476             print (portage.output.bold("\npackage.use:"))
1477             processing_package_use = True
1478             get_recursive_info(USER_CONFIG_PATH + "/package.use")
1479             if "--fix" in cmdline:
1480                 cleanFile(USER_CONFIG_PATH + "/package.use")
1481                 clean_useflagsFile(USER_CONFIG_PATH + "/package.use")
1482             print (portage.output.bold("Done\n"))
1483         elif cmd == "--all":
1484             print (portage.output.bold("\npackage.keywords:"))
1485             get_recursive_info(USER_CONFIG_PATH + "/package.keywords")
1486             print (portage.output.bold("\npackage.accept_keywords:"))
1487             get_recursive_info(USER_CONFIG_PATH + "/package.accept_keywords")
1488             print (portage.output.bold("\npackage.unmask:"))
1489             checking_package_unmask = True
1490             get_recursive_info(USER_CONFIG_PATH + "/package.unmask")
1491             checking_package_unmask = False
1492             checking_package_mask = True
1493             print (portage.output.bold("\npackage.mask:"))
1494             get_recursive_info(USER_CONFIG_PATH + "/package.mask")
1495             print (portage.output.bold("\npackage.use:"))
1496             processing_package_use = True
1497             get_recursive_info(USER_CONFIG_PATH + "/package.use")
1498             if "--fix" in cmdline:
1499                 cleanFile(USER_CONFIG_PATH + "/package.keywords")
1500                 cleanFile(USER_CONFIG_PATH + "/package.accept_keywords")
1501                 cleanFile(USER_CONFIG_PATH + "/package.unmask")
1502                 cleanFile(USER_CONFIG_PATH + "/package.use")
1503                 cleanFile(USER_CONFIG_PATH + "/package.mask")
1504                 clean_useflagsFile(USER_CONFIG_PATH + "/package.use")
1505             print (portage.output.bold("\nDone\n"))
1506
1507     print_overlay_text()
1508
1509     if len(cmdline) == 0:
1510         if show_changes_only_flag or show_removable_only_flag:
1511             if (show_changes_only_flag):
1512                 print (portage.output.green("-c") + " or " + portage.output.green("--changes-only") + " must be accompanied by one of the following:")
1513             else:
1514                 print (portage.output.green("-r") + " or " + portage.output.green("--removable-only") + " must be accompanied by one of the following:")
1515
1516             print ("     " + portage.output.yellow("-k") + " or " + portage.output.yellow("--keyword"))
1517             print ("     " + portage.output.yellow("-u") + " or " + portage.output.yellow("--unmask"))
1518             print ("     " + portage.output.yellow("-m") + " or " + portage.output.yellow("--mask"))
1519             print ("     " + portage.output.yellow("-a") + " or " + portage.output.yellow("--all"))
1520
1521             print_usage()
1522