#! /usr/bin/env python3
# 
# namcap - A Pacman package analyzer
# Copyright (C) 2003-2009 Jason Chu <jason@archlinux.org>
# 
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# 

import getopt
import os
import sys
import tarfile

import Namcap.depends
import Namcap.tags
import Namcap.version

# Functions
def get_modules():
	"""Return all possible modules (rules)"""
	return Namcap.rules.all_rules

def usage():
	"""Display usage information"""
	print("")
	print("Usage: " + sys.argv[0] + " [OPTIONS] packages")
	print("")
	print("Options are:")
	print("    -L, --list                       : list available rules")
	print("    -i                               : prints information (debug) responses from rules")
	print("    -m                               : makes the output parseable (machine-readable)")
	print("    -e rulelist, --exclude=rulelist  : don't apply RULELIST rules to the package")
	print("    -r rulelist, --rules=rulelist    : only apply RULELIST rules to the package")
	print("    -t tags                          : use a custom tag file")
	print("    -v version                       : print version and exit")

	sys.exit(2)

def open_package(filename):
	try:
		tar = tarfile.open(filename, "r")
		if '.PKGINFO' not in tar.getnames():
			tar.close()
			return None
	except IOError:
		if tar:
			tar.close()
		return None
	return tar

def check_rules_exclude(optlist):	
	'''Check if the -r (--rules) and the -r (--exclude) options
	are being used at same time'''
	args_used = 0
	for i in optlist:	
		if '-r' in i or '-e' in i:
			args_used += 1
		if '--rules' in i or '--exclude' in i:
			args_used += 1			
	return args_used

def show_messages(name, key, messages):
	colored_key = {'E': '\033[91mE\033[00m', 'W': '\033[93mW\033[00m', 'I': '\033[92mI\033[00m'}
	for msg in messages:
		if sys.stdout.isatty():
			print("%s %s: %s" % (name, colored_key[key], Namcap.tags.format_message(msg)))
		else:
			print("%s %s: %s" % (name, key, Namcap.tags.format_message(msg)))

def process_realpackage(package, modules):
	"""Runs namcap checks over a package tarball"""
	extracted = 0
	pkgtar = open_package(package)

	if not pkgtar:
		print("Error: %s is empty or is not a valid package" % package)
		return 1

	pkginfo = Namcap.package.load_from_tarball(package)
	# Loop through each one, load them apply if possible
	for i in modules:
		rule = get_modules()[i]()

		if isinstance(rule, Namcap.ruleclass.PkgInfoRule):
			rule.analyze(pkginfo, None)
		elif isinstance(rule, Namcap.ruleclass.PkgbuildRule):
			pass
		elif isinstance(rule, Namcap.ruleclass.TarballRule):
			rule.analyze(pkginfo, pkgtar)
		else:
			show_messages(pkginfo["name"], 'E',
			              [('error-running-rule %s', i)])

		# Output the three types of messages
		show_messages(pkginfo["name"], 'E', rule.errors)
		show_messages(pkginfo["name"], 'W', rule.warnings)
		if info_reporting:
			show_messages(pkginfo["name"], 'I', rule.infos)
	
	# dependency analysis
	errs, warns, infos = Namcap.depends.analyze_depends(pkginfo)
	show_messages(pkginfo["name"], 'E', errs)
	show_messages(pkginfo["name"], 'W', warns)
	if info_reporting:
		show_messages(pkginfo["name"], 'I', infos)

def process_pkginfo(pkginfo, modules):
	"""Runs namcap checks of a single, non-split PacmanPackage object"""
	for i in modules:
		rule = get_modules()[i]()
		if isinstance(rule, Namcap.ruleclass.PkgInfoRule):
			ret = rule.analyze(pkginfo, None)

		# Output the messages
		if "base" in pkginfo:
			name = "PKGBUILD (" + pkginfo["base"] + ")"
		else:
			name = "PKGBUILD (" + pkginfo["name"] + ")"
		show_messages(name, 'E', rule.errors)
		show_messages(name, 'W', rule.warnings)
		if info_reporting:
			show_messages(name, 'I', rule.infos)


def process_pkgbuild(package, modules):
	"""Runs namcap checks over a PKGBUILD"""
	# We might want to do some verifying in here... but really... isn't that
	# what pacman.load is for?
	pkginfo = Namcap.package.load_from_pkgbuild(package)

	if pkginfo == None:
		print("Error: %s is not a valid PKGBUILD" % package)
		return 1

	# apply global PKGBUILD rules
	for i in modules:
		rule = get_modules()[i]()
		if isinstance(rule, Namcap.ruleclass.PkgbuildRule):
			ret = rule.analyze(pkginfo, package)
		# Output the messages
		if "base" in pkginfo:
			name = "PKGBUILD (" + pkginfo["base"] + ")"
		else:
			name = "PKGBUILD (" + pkginfo["name"] + ")"
		show_messages(name, 'E', rule.errors)
		show_messages(name, 'W', rule.warnings)
		if info_reporting:
			show_messages(name, 'I', rule.infos)
	# apply per pkginfo rule
	for subpkg in (pkginfo.subpackages
			if pkginfo.is_split else [pkginfo]):
		process_pkginfo(subpkg, modules)

# Main
modules = get_modules()
info_reporting = 0
machine_readable = False
filename = None

# get our options and process them
try:
	optlist, args = getopt.getopt(sys.argv[1:], "ihmr:e:t:Lv",
			["info", "help", "machine-readable", "rules=",
				"exclude=", "tags=", "list", "version"])
except getopt.GetoptError:
	usage()

active_modules = {}

# Verifying if we are using the -r and -r options at same time
if check_rules_exclude(optlist) > 1:
	print("You cannot use '-r' (--rules) and '-e' (-exclude) options at same time")
	usage()

for i, k in optlist:
	if i in ('-L', '--list'):
		print("-"*20 + " Namcap rule list " + "-"*20)
		for j in sorted(modules):
			print("%-20s: %s" % (j, modules[j].description))
		sys.exit(2)

	if i in ('-r', '--rules'):
		module_list = k.split(',')
		for j in module_list:
			if j in modules:
				active_modules[j] = modules[j]
			else:
				print("Error: Rule '%s' does not exist" % j)
				usage()

	# Used to exclude some rules from the check
	if i in ('-e', '--exclude'):
		module_list = k.split(',')
		active_modules.update(modules)
		for j in module_list:
			if j in modules:
				active_modules.pop(j)
			else:
				print("Error: Rule '%s' does not exist" % j)
				usage()

	if i in ('-i', '--info'):
		info_reporting = 1

	if i in ('-h', '--help'):
		usage()
	if i in ('-m', '--machine-readable'):
		machine_readable = True

	if i in ('-t', '--tags'):
		filename = k

	if i in ('-v', '--version'):
		print(Namcap.version.get_version())
		sys.exit(0)

# If there are no args, print usage
if (args == []):
	usage()

Namcap.tags.load_tags(filename = filename, machine = machine_readable)

packages = args

# No rules selected?  Then select them all!
if len(active_modules) == 0:
	active_modules = modules

# Go through each package, get the info, and apply the rules
for package in packages:
	if not os.access(package, os.R_OK):
		print("Error: Problem reading %s" % package)
		usage()

	if os.path.isfile(package) and tarfile.is_tarfile(package):
		process_realpackage(package, active_modules)
	elif 'PKGBUILD' in package:
		process_pkgbuild(package, active_modules)
	else:
		print("Error: %s not package or PKGBUILD" % package)

# vim: set ts=4 sw=4 noet:
