1/*
2 * Copyright 2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
8
9
10#include <getopt.h>
11#include <package/manager/Exceptions.h>
12#include <ObjectList.h>
13#include <package/solver/SolverPackage.h>
14#include <package/solver/SolverPackageSpecifier.h>
15#include <package/solver/SolverPackageSpecifierList.h>
16#include <stdio.h>
17#include <stdlib.h>
18
19#include "Command.h"
20#include "pkgman.h"
21#include "PackageManager.h"
22
23
24// TODO: internationalization!
25
26
27using namespace BPackageKit;
28using namespace BPackageKit::BPrivate;
29using namespace BPackageKit::BManager::BPrivate;
30
31
32static const char* const kShortUsage =
33	"  %command% <package> ...\n"
34	"    Installs one or more packages.\n";
35
36static const char* const kLongUsage =
37	"Usage: %program% %command% <package> ...\n"
38	"Installs the specified packages. A <package> argument can be a search\n"
39	"string by which the package is looked up in a remote repository or a\n"
40	"path to a local package file. In the latter case the file is copied.\n"
41	"\n"
42	"Options:\n"
43	"  --debug <level>\n"
44	"    Print debug output. <level> should be between 0 (no debug output,\n"
45	"    the default) and 10 (most debug output).\n"
46	"  -H, --home\n"
47	"    Install the packages in the user's home directory. Default is to\n"
48	"    install in the system directory.\n"
49	"  -y\n"
50	"    Non-interactive mode. Automatically confirm changes, but fail when\n"
51	"    encountering problems.\n"
52	"\n";
53
54
55DEFINE_COMMAND(InstallCommand, "install", kShortUsage, kLongUsage,
56	COMMAND_CATEGORY_PACKAGES)
57
58
59int
60InstallCommand::Execute(int argc, const char* const* argv)
61{
62	BPackageInstallationLocation location
63		= B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
64	bool interactive = true;
65
66	while (true) {
67		static struct option sLongOptions[] = {
68			{ "debug", required_argument, 0, OPTION_DEBUG },
69			{ "help", no_argument, 0, 'h' },
70			{ "home", no_argument, 0, 'H' },
71			{ 0, 0, 0, 0 }
72		};
73
74		opterr = 0; // don't print errors
75		int c = getopt_long(argc, (char**)argv, "hHy", sLongOptions, NULL);
76		if (c == -1)
77			break;
78
79		if (fCommonOptions.HandleOption(c))
80			continue;
81
82		switch (c) {
83			case 'h':
84				PrintUsageAndExit(false);
85				break;
86
87			case 'H':
88				location = B_PACKAGE_INSTALLATION_LOCATION_HOME;
89				break;
90
91			case 'y':
92				interactive = false;
93				break;
94
95			default:
96				PrintUsageAndExit(true);
97				break;
98		}
99	}
100
101	// The remaining arguments are the packages to be installed.
102	if (argc < optind + 1)
103		PrintUsageAndExit(true);
104
105	int packageCount = argc - optind;
106	const char* const* packages = argv + optind;
107
108	// perform the installation
109	PackageManager packageManager(location, interactive);
110	packageManager.SetDebugLevel(fCommonOptions.DebugLevel());
111	try {
112		packageManager.Install(packages, packageCount);
113	} catch (BNothingToDoException&) {
114		// Output already installed packages
115		BSolverPackageSpecifierList packagesToInstall;
116		if (!packagesToInstall.AppendSpecifiers(packages, packageCount))
117			throw std::bad_alloc();
118		// Find the installed packages that match the specification
119		const BSolverPackageSpecifier* unmatchedSpecifier;
120		BObjectList<BSolverPackage> installedPackages;
121		packageManager.Solver()->FindPackages(packagesToInstall,
122			BSolver::B_FIND_INSTALLED_ONLY,
123			installedPackages, &unmatchedSpecifier);
124
125		for (int32 i = 0; BSolverPackage* package = installedPackages.ItemAt(i);
126			i++) {
127			BString repository;
128			if (dynamic_cast<PackageManager::MiscLocalRepository*>(
129					package->Repository()) != NULL)
130				repository = "local file";
131			else
132				repository.SetToFormat(
133					"repository %s", package->Repository()->Name().String());
134			fprintf(stderr, "%s from %s is already installed.\n",
135				package->VersionedName().String(),
136				repository.String());
137		}
138		throw;
139	}
140
141	return 0;
142}
143