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