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