#!/usr/bin/python -b
# Copyright 2013-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

import os
import stat
import sys
import subprocess
import traceback

import portage
from portage.util._argparse import ArgumentParser
from portage.util.movefile import _copyxattr
from portage.exception import OperationNotSupported

# Change back to original cwd _after_ all imports (bug #469338).
os.chdir(os.environ["__PORTAGE_HELPER_CWD"])

def parse_args(args):
	"""
	Parse the command line arguments using optparse for python 2.6 compatibility
	Args:
	  args: a list of the white space delimited command line
	Returns:
	  tuple of the Namespace of parsed options, and a list of order parameters
	"""
	parser = ArgumentParser(add_help=False)

	parser.add_argument(
		"-b",
		action="store_true",
		dest="shortopt_b"
	)
	parser.add_argument(
		"--backup",
		action="store",
		dest="backup"
		)
	parser.add_argument(
		"-c",
		action="store_true",
		dest="shortopt_c"
	)
	parser.add_argument(
		"--compare",
		"-C",
		action="store_true",
		dest="compare"
	)
	parser.add_argument(
		"--directory",
			"-d",
		action="store_true",
		dest="directory"
	)
	parser.add_argument(
		"-D",
		action="store_true",
		dest="shortopt_D"
	)
	parser.add_argument(
		"--owner",
		"-o",
		action="store",
		dest="owner"
	)
	parser.add_argument(
		"--group",
		"-g",
		action="store",
		dest="group"
	)
	parser.add_argument(
		"--mode",
		"-m",
		action="store",
		dest="mode"
	)
	parser.add_argument(
		"--preserve-timestamps",
		"-p",
		action="store_true",
		dest="preserve_timestamps"
	)
	parser.add_argument(
		"--strip",
		"-s",
		action="store_true",
		dest="strip"
	)
	parser.add_argument(
		"--strip-program",
		action="store",
		dest="strip_program"
	)
	parser.add_argument(
		"--suffix",
		"-S",
		action="store",
		dest="suffix"
	)
	parser.add_argument(
		"--target-directory",
		"-t",
		action="store",
		dest="target_directory"
	)
	parser.add_argument(
		"--no-target-directory",
		"-T",
		action="store_true",
		dest="no_target_directory"
	)
	parser.add_argument(
		"--context",
		"-Z",
		action="store",
		dest="context"
	)
	parser.add_argument(
		"--verbose",
		"-v",
		action="store_true",
		dest="verbose"
	)
	parser.add_argument(
		"--help",
		action="store_true",
		dest="help"
	)
	parser.add_argument(
		"--version",
		action="store_true",
		dest="version"
	)

	# Use parse_known_args for maximum compatibility with
	# getopt handling of non-option file arguments. Note
	# that parser.add_argument("files", nargs='+') would
	# be subtly incompatible because it requires that all
	# of the file arguments be grouped sequentially. Also
	# note that we have to explicitly call add_argument
	# for known options in order for argparse to correctly
	# separate option arguments from file arguments in all
	# cases (it also allows for optparse compatibility).
	parsed_args = parser.parse_known_args()

	opts  = parsed_args[0]
	files = parsed_args[1]
	files = [f for f in files if f != "--"]	# filter out "--"

	return (opts, files)


def copy_xattrs(opts, files):
	"""
	Copy the extended attributes using portage.util.movefile._copyxattr
	Args:
	  opts:  Namespace of the parsed command line otions
	  files: list of ordered command line parameters which should be files/directories
	Returns:
	  system exit code
	"""
	if opts.directory or not files:
		return os.EX_OK

	if opts.target_directory is None:
		source, target = files[:-1], files[-1]
		target_is_directory = os.path.isdir(target)
	else:
		source, target = files, opts.target_directory
		target_is_directory = True

	exclude = os.environ.get("PORTAGE_XATTR_EXCLUDE", "")

	try:
		if target_is_directory:
			for s in source:
				abs_path = os.path.join(target, os.path.basename(s))
				_copyxattr(s, abs_path, exclude=exclude)
		else:
			_copyxattr(source[0], target, exclude=exclude)
		return os.EX_OK

	except OperationNotSupported:
		traceback.print_exc()
		return os.EX_OSERR


def Which(filename, path=None, exclude=None):
	"""
	Find the absolute path of 'filename' in a given search 'path'
	Args:
	  filename: basename of the file
	  path: colon delimited search path
	  exclude: path of file to exclude
	"""
	if path is None:
		path = os.environ.get('PATH', '')

	if exclude is not None:
		st = os.stat(exclude)
		exclude = (st.st_ino, st.st_dev)

	for p in path.split(':'):
		p = os.path.join(p, filename)
		if os.access(p, os.X_OK):
			try:
				st = os.stat(p)
			except OSError:
				# file disappeared?
				pass
			else:
				if stat.S_ISREG(st.st_mode) and \
					(exclude is None or exclude != (st.st_ino, st.st_dev)):
					return p

	return None


def main(args):
	opts, files = parse_args(args)
	install_binary = Which('install', exclude=os.environ["__PORTAGE_HELPER_PATH"])
	if install_binary is None:
		sys.stderr.write("install: command not found\n")
		return 127

	cmdline = [install_binary]
	cmdline += args

	if sys.hexversion >= 0x3000000:
		# We can't trust that the filesystem encoding (locale dependent)
		# correctly matches the arguments, so use surrogateescape to
		# pass through the original argv bytes for Python 3.
		fs_encoding = sys.getfilesystemencoding()
		cmdline = [x.encode(fs_encoding, 'surrogateescape') for x in cmdline]
		files = [x.encode(fs_encoding, 'surrogateescape') for x in files]
		if opts.target_directory is not None:
			opts.target_directory = \
				opts.target_directory.encode(fs_encoding, 'surrogateescape')

	returncode = subprocess.call(cmdline)
	if returncode == os.EX_OK:
		returncode = copy_xattrs(opts, files)
		if returncode != os.EX_OK:
			portage.util.writemsg("!!! install: copy_xattrs failed with the "
				"following arguments: %s\n" %
				" ".join(portage._shell_quote(x) for x in args), noiselevel=-1)
	return returncode


if __name__ == "__main__":
	sys.exit(main(sys.argv[1:]))
