⛏️ index : haiku.git

/*
 * Copyright 2013, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Ingo Weinhold <ingo_weinhold@gmx.de>
 */


#include <getopt.h>
#include <package/manager/Exceptions.h>
#include <ObjectList.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverPackageSpecifier.h>
#include <package/solver/SolverPackageSpecifierList.h>
#include <stdio.h>
#include <stdlib.h>

#include "Command.h"
#include "pkgman.h"
#include "PackageManager.h"


// TODO: internationalization!


using namespace BPackageKit;
using namespace BPackageKit::BPrivate;
using namespace BPackageKit::BManager::BPrivate;


static const char* const kShortUsage =
	"  %command% <package> ...\n"
	"    Installs one or more packages.\n";

static const char* const kLongUsage =
	"Usage: %program% %command% <package> ...\n"
	"Installs the specified packages. A <package> argument can be a search\n"
	"string by which the package is looked up in a remote repository or a\n"
	"path to a local package file. In the latter case the file is copied.\n"
	"\n"
	"Options:\n"
	"  --debug <level>\n"
	"    Print debug output. <level> should be between 0 (no debug output,\n"
	"    the default) and 10 (most debug output).\n"
	"  -H, --home\n"
	"    Install the packages in the user's home directory. Default is to\n"
	"    install in the system directory.\n"
	"  -R, --no-refresh\n"
	"    Do not refresh the repository caches.\n"
	"  -y\n"
	"    Non-interactive mode. Automatically confirm changes, but fail when\n"
	"    encountering problems.\n"
	"\n";


DEFINE_COMMAND(InstallCommand, "install", kShortUsage, kLongUsage,
	COMMAND_CATEGORY_PACKAGES)


int
InstallCommand::Execute(int argc, const char* const* argv)
{
	BPackageInstallationLocation location
		= B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
	bool interactive = true;
	bool refresh = true;

	while (true) {
		static struct option sLongOptions[] = {
			{ "debug", required_argument, 0, OPTION_DEBUG },
			{ "help", no_argument, 0, 'h' },
			{ "home", no_argument, 0, 'H' },
			{ "no-refresh", no_argument, 0, 'R' },
			{ 0, 0, 0, 0 }
		};

		opterr = 0; // don't print errors
		int c = getopt_long(argc, (char**)argv, "hHRy", sLongOptions, NULL);
		if (c == -1)
			break;

		if (fCommonOptions.HandleOption(c))
			continue;

		switch (c) {
			case 'h':
				PrintUsageAndExit(false);
				break;

			case 'H':
				location = B_PACKAGE_INSTALLATION_LOCATION_HOME;
				break;

			case 'y':
				interactive = false;
				break;

			case 'R':
				refresh = false;
				break;

			default:
				PrintUsageAndExit(true);
				break;
		}
	}

	// The remaining arguments are the packages to be installed.
	if (argc < optind + 1)
		PrintUsageAndExit(true);

	int packageCount = argc - optind;
	const char* const* packages = argv + optind;

	// perform the installation
	PackageManager packageManager(location, interactive);
	packageManager.SetDebugLevel(fCommonOptions.DebugLevel());
	try {
		packageManager.Install(packages, packageCount, refresh);
	} catch (BNothingToDoException&) {
		// Output already installed packages
		BSolverPackageSpecifierList packagesToInstall;
		if (!packagesToInstall.AppendSpecifiers(packages, packageCount))
			throw std::bad_alloc();
		// Find the installed packages that match the specification
		const BSolverPackageSpecifier* unmatchedSpecifier;
		BObjectList<BSolverPackage> installedPackages;
		packageManager.Solver()->FindPackages(packagesToInstall,
			BSolver::B_FIND_INSTALLED_ONLY,
			installedPackages, &unmatchedSpecifier);

		for (int32 i = 0; BSolverPackage* package = installedPackages.ItemAt(i);
			i++) {
			BString repository;
			if (dynamic_cast<PackageManager::MiscLocalRepository*>(
					package->Repository()) != NULL)
				repository = "local file";
			else
				repository.SetToFormat(
					"repository %s", package->Repository()->Name().String());
			fprintf(stderr, "%s from %s is already installed.\n",
				package->VersionedName().String(),
				repository.String());
		}
		throw;
	}

	return 0;
}