rpm-razor.c
author Kristian H?gsberg <krh@redhat.com>
Mon Jun 09 12:47:37 2008 -0400 (2008-06-09)
changeset 230 c1e2aed8dd07
parent 222 052dce887a07
child 235 060d83d8eca9
permissions -rw-r--r--
Rewrite depsolver to use a series of passes over all packages.

The big change is that we follow one step of the depedency chain for
each package to resolve in each iteration, and repeat until there are
no more possible moves. In contrast the old depsolver would try to
follow the dependency chain completely for one package at a time.

This new approach is simpler and faster, and at the same time more
roboust. Instead of knowing how one newly installed package may
affect other packages (obsoleting, pulling in new packages etc), the
new algorithm just looks at the total list of requires, provides,
obsoletes and conflicts after installing new packages.
krh@215
     1
/*
krh@215
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
krh@215
     3
 * Copyright (C) 2008  Red Hat, Inc
krh@215
     4
 *
krh@215
     5
 * This program is free software; you can redistribute it and/or modify
krh@215
     6
 * it under the terms of the GNU General Public License as published by
krh@215
     7
 * the Free Software Foundation; either version 2 of the License, or
krh@215
     8
 * (at your option) any later version.
krh@215
     9
 *
krh@215
    10
 * This program is distributed in the hope that it will be useful,
krh@215
    11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
krh@215
    12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
krh@215
    13
 * GNU General Public License for more details.
krh@215
    14
 *
krh@215
    15
 * You should have received a copy of the GNU General Public License along
krh@215
    16
 * with this program; if not, write to the Free Software Foundation, Inc.,
krh@215
    17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
krh@215
    18
 */
krh@215
    19
krh@215
    20
#include <stdlib.h>
krh@215
    21
#include <string.h>
krh@215
    22
#include <stdio.h>
krh@215
    23
#include "razor.h"
krh@215
    24
krh@215
    25
enum option_type {
krh@215
    26
	OPTION_LAST,
krh@215
    27
	OPTION_GROUP,
krh@215
    28
	OPTION_BOOL,
krh@215
    29
	OPTION_STRING
krh@215
    30
};
krh@215
    31
krh@215
    32
struct option {
krh@215
    33
	enum option_type type;
krh@215
    34
	const char *name;
krh@215
    35
	char short_name;
krh@215
    36
	const char *arg_name;
krh@215
    37
	const char *description;
krh@215
    38
	void *data;
krh@215
    39
};
krh@215
    40
krh@218
    41
/* A note about all these options: rpm allows options to mean
krh@218
    42
 * different things depending on what other options are present on the
krh@218
    43
 * command line.  For example, if -q or --query is present, -i no
krh@218
    44
 * longer means install, but info.  The way we handle this is by
krh@218
    45
 * setting all the options that may match (ie if -i is given we set
krh@218
    46
 * install and info), and then look at the relevent one depending on
krh@218
    47
 * what else in on the command line. */
krh@218
    48
krh@216
    49
static int option_all, option_whatrequires, option_whatprovides;
krh@217
    50
static int option_package;
krh@215
    51
krh@215
    52
static const struct option query_options[] = {
krh@215
    53
	{ OPTION_BOOL, "configfiles", 'c', NULL, "list all configuration files", NULL },
krh@215
    54
	{ OPTION_BOOL, "docfiles", 'd', NULL, "list all documentation files", NULL },
krh@215
    55
	{ OPTION_BOOL, "dump", 0, NULL, "dump basic file information", NULL },
krh@215
    56
	{ OPTION_BOOL, "list", 0, NULL, "list files in package", NULL },
krh@215
    57
	{ OPTION_STRING, "queryformat", 0, "QUERYFORMAT", "use the following query format", NULL },
krh@215
    58
	{ OPTION_BOOL, "state", 's', NULL, "display the states of the listed files", NULL },
krh@215
    59
	{ OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all },
krh@215
    60
	{ OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", NULL },
krh@215
    61
	{ OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL },
krh@217
    62
	{ OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package },
krh@215
    63
	{ OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL },
krh@215
    64
	{ OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL },
krh@215
    65
	{ OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL },
krh@215
    66
	{ OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL },
krh@215
    67
	{ OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL, },
krh@215
    68
	{ OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL },
krh@216
    69
	{ OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires },
krh@216
    70
	{ OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides },
krh@215
    71
	{ OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL },
krh@215
    72
	{ }
krh@215
    73
};
krh@215
    74
krh@215
    75
static const struct option verify_options[] = {
krh@215
    76
	{ OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL },
krh@215
    77
	{ OPTION_BOOL, "nofiles", 0, NULL, "don't verify files in package", NULL },
krh@215
    78
	{ OPTION_BOOL, "nodeps", 0, NULL, "don't verify package dependencies", NULL },
krh@215
    79
	{ OPTION_BOOL, "noscript", 0, NULL, "don't execute verify script(s)", NULL, },
krh@222
    80
	{ OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all },
krh@215
    81
	{ OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", NULL },
krh@215
    82
	{ OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL },
krh@222
    83
	{ OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package },
krh@215
    84
	{ OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL },
krh@215
    85
	{ OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL },
krh@215
    86
	{ OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL },
krh@215
    87
	{ OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL },
krh@215
    88
	{ OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL },
krh@215
    89
	{ OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL },
krh@216
    90
	{ OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires },
krh@216
    91
	{ OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides },
krh@215
    92
	{ OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL },
krh@215
    93
	{ }
krh@215
    94
};
krh@215
    95
krh@215
    96
static const struct option ftw_options[] = {
krh@215
    97
	{ OPTION_BOOL, "comfollow", 0, NULL, "FTS_COMFOLLOW: follow command line symlinks", NULL },
krh@215
    98
	{ OPTION_BOOL, "logical", 0, NULL, "FTS_LOGICAL: logical walk", NULL },
krh@215
    99
	{ OPTION_BOOL, "nochdir", 0, NULL, "FTS_NOCHDIR: don't change directories", NULL },
krh@215
   100
	{ OPTION_BOOL, "nostat", 0, NULL, "FTS_NOSTAT: don't get stat info", NULL },
krh@215
   101
	{ OPTION_BOOL, "physical", 0, NULL, "FTS_PHYSICAL: physical walk", NULL },
krh@215
   102
	{ OPTION_BOOL, "seedot", 0, NULL, "FTS_SEEDOT: return dot and dot-dot", NULL },
krh@215
   103
	{ OPTION_BOOL, "xdev", 0, NULL, "FTS_XDEV: don't cross devices", NULL },
krh@215
   104
	{ OPTION_BOOL, "whiteout", 0, NULL, "FTS_WHITEOUT: return whiteout information", NULL },
krh@215
   105
	{ }
krh@215
   106
};
krh@215
   107
krh@215
   108
static const struct option signature_options[] = {
krh@215
   109
	{ OPTION_BOOL, "addsign", 0, NULL, "sign package(s) (identical to --resign)", NULL, },
krh@215
   110
	{ OPTION_BOOL, "checksig", 'K', NULL, "verify package signature(s)", NULL, },
krh@215
   111
	{ OPTION_BOOL, "delsign", 0, NULL, "delete package signatures", NULL, },
krh@215
   112
	{ OPTION_BOOL, "import", 0, NULL, "import an armored public key", NULL, },
krh@215
   113
	{ OPTION_BOOL, "resign", 0, NULL, "sign package(s) (identical to --addsign)", NULL, },
krh@215
   114
	{ OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, },
krh@215
   115
	{ OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL },
krh@215
   116
	{ }
krh@215
   117
};
krh@215
   118
krh@215
   119
static const struct option database_options[] = {
krh@215
   120
	{ OPTION_BOOL, "initdb", 0, NULL, "initialize database", NULL },
krh@215
   121
	{ OPTION_BOOL, "rebuilddb", 0, NULL, "rebuild database inverted lists from installed package headers", NULL },
krh@215
   122
	{ }
krh@215
   123
};
krh@215
   124
krh@215
   125
static int option_erase, option_install, option_upgrade;
krh@215
   126
krh@215
   127
static const struct option install_options[] = {
krh@215
   128
	{ OPTION_BOOL, "aid", 0, NULL, "add suggested packages to transaction", NULL, },
krh@215
   129
	{ OPTION_BOOL, "allfiles", 0, NULL, "install all files, even configurations which might otherwise be skipped", NULL, },
krh@215
   130
	{ OPTION_BOOL, "allmatches", 0, NULL, "remove all packages which match <package> (normally an error is generated if <package> specified multiple packages)", NULL, },
krh@215
   131
	{ OPTION_BOOL, "badreloc", 0, NULL, "relocate files in non-relocatable package", NULL },
krh@215
   132
	{ OPTION_BOOL, "erase", 'e', "<package>", "erase (uninstall) package", &option_erase },
krh@215
   133
	{ OPTION_BOOL, "excludedocs", 0, NULL, "do not install documentation", NULL, },
krh@215
   134
	{ OPTION_BOOL, "excludepath", 0, "<path>", "skip files with leading component <path> ", NULL, },
krh@215
   135
	{ OPTION_BOOL, "fileconflicts", 0, NULL, "detect file conflicts between packages", NULL, },
krh@215
   136
	{ OPTION_BOOL, "force", 0, NULL, "short hand for --replacepkgs --replacefiles", NULL },
krh@215
   137
	{ OPTION_BOOL, "freshen", 'F', "<packagefile>+", "upgrade package(s) if already installed", NULL },
krh@215
   138
	{ OPTION_BOOL, "hash", 'h', NULL, "print hash marks as package installs (good with -v)", NULL },
krh@215
   139
	{ OPTION_BOOL, "ignorearch", 0, NULL, "don't verify package architecture", NULL, },
krh@215
   140
	{ OPTION_BOOL, "ignoreos", 0, NULL, "don't verify package operating system", NULL, },
krh@215
   141
	{ OPTION_BOOL, "ignoresize", 0, NULL, "don't check disk space before installing", NULL },
krh@215
   142
	{ OPTION_BOOL, "install", 'i', NULL, "install package(s)", &option_install },
krh@215
   143
	{ OPTION_BOOL, "justdb", 0, NULL, "update the database, but do not modify the filesystem", NULL, },
krh@215
   144
	{ OPTION_BOOL, "nodeps", 0, NULL, "do not verify package dependencies", NULL, },
krh@215
   145
	{ OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL, },
krh@215
   146
	{ OPTION_BOOL, "nocontexts", 0, NULL, "don't install file security contexts", NULL, },
krh@215
   147
	{ OPTION_BOOL, "noorder", 0, NULL, "do not reorder package installation to satisfy dependencies", NULL, },
krh@215
   148
	{ OPTION_BOOL, "nosuggest", 0, NULL, "do not suggest missing dependency resolution(s)", NULL, },
krh@215
   149
	{ OPTION_BOOL, "noscripts", 0, NULL, "do not execute package scriptlet(s)", NULL, },
krh@215
   150
	{ OPTION_BOOL, "notriggers", 0, NULL, "do not execute any scriptlet(s) triggered by this package", NULL, },
krh@215
   151
	{ OPTION_BOOL, "oldpackage", 0, NULL, "upgrade to an old version of the package (--force on upgrades does this automatically)", NULL },
krh@215
   152
	{ OPTION_BOOL, "percent", 0, NULL, "print percentages as package installs", NULL, },
krh@215
   153
	{ OPTION_STRING, "prefix", 0, "<dir>", "relocate the package to <dir>, if relocatable", NULL, },
krh@215
   154
	{ OPTION_STRING, "relocate", 0, "<old>=<new>", "relocate files from path <old> to <new>", NULL, },
krh@215
   155
	{ OPTION_BOOL, "repackage", 0, NULL, "save erased package files by repackaging", NULL, },
krh@215
   156
	{ OPTION_BOOL, "replacefiles", 0, NULL, "ignore file conflicts between packages", NULL, },
krh@215
   157
	{ OPTION_BOOL, "replacepkgs", 0, NULL, "reinstall if the package is already present", NULL, },
krh@215
   158
	{ OPTION_BOOL, "test", 0, NULL, "don't install, but tell if it would work or not", NULL },
krh@215
   159
	{ OPTION_BOOL, "upgrade", 'U', "<packagefile>+", "upgrade package(s)", &option_upgrade },
krh@215
   160
	{ }
krh@215
   161
};
krh@215
   162
krh@215
   163
static int option_version;
krh@215
   164
krh@215
   165
static const struct option common_options[] = {
krh@215
   166
	{ OPTION_STRING, "define", 'D', "MACRO EXPR", "define MACRO with value EXPR", NULL, },
krh@215
   167
	{ OPTION_STRING, "eval", 'E', "EXPR", "print macro expansion of EXPR", NULL },
krh@215
   168
	{ OPTION_STRING, "macros", 0, "<FILE:...>", "read <FILE:...> instead of default file(s)", NULL },
krh@215
   169
	{ OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, },
krh@215
   170
	{ OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL, },
krh@215
   171
	{ OPTION_STRING, "rcfile", 0, "<FILE:...>", "read <FILE:...> instead of default file(s)", NULL },
krh@215
   172
	{ OPTION_STRING, "root", 'r', "ROOT", "use ROOT as top level directory (default: \"/\")", NULL },
krh@215
   173
	{ OPTION_BOOL, "querytags", 0, NULL, "display known query tags", NULL, },
krh@215
   174
	{ OPTION_BOOL, "showrc", 0, NULL, "display final rpmrc and macro configuration", NULL, },
krh@215
   175
	{ OPTION_BOOL, "quiet", 0, NULL, "provide less detailed output", NULL },
krh@215
   176
	{ OPTION_BOOL, "verbose", 'v', NULL, "provide more detailed output", NULL },
krh@215
   177
	{ OPTION_BOOL, "version", 0, NULL, "print the version of rpm being used", &option_version },
krh@215
   178
	{ }
krh@215
   179
};
krh@215
   180
krh@218
   181
static int option_conflicts, option_obsoletes, option_requires;
krh@218
   182
static int option_provides, option_info, option_changelog;
krh@216
   183
krh@215
   184
static const struct option alias_options[] = {
krh@215
   185
	{ OPTION_BOOL, "scripts", 0, NULL, "list install/erase scriptlets from package(s)", NULL, },
krh@215
   186
	{ OPTION_BOOL, "setperms", 0, NULL, "set permissions of files in a package", NULL, },
krh@215
   187
	{ OPTION_BOOL, "setugids", 0, NULL, "set user/group ownership of files in a package", NULL, },
krh@216
   188
	{ OPTION_BOOL, "conflicts", 0, NULL, "list capabilities this package conflicts with", &option_conflicts, },
krh@216
   189
	{ OPTION_BOOL, "obsoletes", 0, NULL, "list other packages removed by installing this package", &option_obsoletes, },
krh@216
   190
	{ OPTION_BOOL, "provides", 0, NULL, "list capabilities that this package provides", &option_provides, },
krh@216
   191
	{ OPTION_BOOL, "requires", 0, NULL, "list capabilities required by package(s)", &option_requires, },
krh@218
   192
	{ OPTION_BOOL, "info", 'i', NULL, "list descriptive information from package(s)", &option_info, },
krh@218
   193
	{ OPTION_BOOL, "changelog", 0, NULL, "list change logs for this package", &option_changelog, },
krh@215
   194
	{ OPTION_BOOL, "xml", 0, NULL, "list metadata in xml", NULL, },
krh@215
   195
	{ OPTION_BOOL, "triggers", 0, NULL, "list trigger scriptlets from package(s)", NULL, },
krh@215
   196
	{ OPTION_BOOL, "last", 0, NULL, "list package(s) by install time, most recent first", NULL, },
krh@215
   197
	{ OPTION_BOOL, "dupes", 0, NULL, "list duplicated packages", NULL, },
krh@215
   198
	{ OPTION_BOOL, "filesbypkg", 0, NULL, "list all files from each package", NULL, },
krh@215
   199
	{ OPTION_BOOL, "fileclass", 0, NULL, "list file names with classes", NULL, },
krh@215
   200
	{ OPTION_BOOL, "filecolor", 0, NULL, "list file names with colors", NULL, },
krh@215
   201
	{ OPTION_BOOL, "filecontext", 0, NULL, "list file names with security context from header", NULL, },
krh@215
   202
	{ OPTION_BOOL, "fscontext", 0, NULL, "list file names with security context from file system", NULL, },
krh@215
   203
	{ OPTION_BOOL, "recontext", 0, NULL, "list file names with security context from policy RE", NULL, },
krh@215
   204
	{ OPTION_BOOL, "fileprovide", 0, NULL, "list file names with provides", NULL, },
krh@215
   205
	{ OPTION_BOOL, "filerequire", 0, NULL, "list file names with requires", NULL, },
krh@215
   206
	{ OPTION_BOOL, "redhatprovides", 0, NULL, "find package name that contains a provided capability (needs rpmdb-redhat package installed)", NULL, },
krh@215
   207
	{ OPTION_BOOL, "redhatrequires", 0, NULL, "find package name that contains a required capability (needs rpmdb-redhat package installed)", NULL, },
krh@215
   208
	{ OPTION_STRING, "buildpolicy", 0, "<policy>", "set buildroot <policy> (e.g. compress man pages)", NULL, },
krh@215
   209
	{ OPTION_BOOL, "with", 0, "<option>", "enable configure <option> for build", NULL, },
krh@215
   210
	{ OPTION_BOOL, "without", 0, "<option>", "disable configure <option> for build", NULL },
krh@215
   211
	{ }
krh@215
   212
};
krh@215
   213
krh@215
   214
static int option_help, option_usage;
krh@215
   215
krh@215
   216
static const struct option help_options[] = {
krh@215
   217
	{ OPTION_BOOL, "help", '?', NULL, "Show this help message", &option_help },
krh@215
   218
	{ OPTION_BOOL, "usage", 0, NULL, "Display brief usage message", &option_usage},
krh@215
   219
	{ }
krh@215
   220
};
krh@215
   221
krh@215
   222
static int option_query, option_verify;
krh@215
   223
krh@215
   224
static const struct option rpm_options[] = {
krh@215
   225
	{ OPTION_BOOL, "query", 'q', NULL, "Query rpm database", &option_query },
krh@215
   226
	{ OPTION_BOOL, "verify", 'V', NULL, "Verify rpm database", &option_verify },
krh@215
   227
	{ OPTION_GROUP, NULL, 0, NULL, "Query options (with -q or --query):", &query_options },
krh@215
   228
	{ OPTION_GROUP, NULL, 0, NULL, "Verify options (with -V or --verify):", &verify_options },
krh@215
   229
	{ OPTION_GROUP, NULL, 0, NULL, "File tree walk options (with --ftswalk):", &ftw_options },
krh@215
   230
	{ OPTION_GROUP, NULL, 0, NULL, "Signature options:", &signature_options },
krh@215
   231
	{ OPTION_GROUP, NULL, 0, NULL, "Database options:", &database_options },
krh@215
   232
	{ OPTION_GROUP, NULL, 0, NULL, "Install/Upgrade/Erase options:", &install_options },
krh@215
   233
	{ OPTION_GROUP, NULL, 0, NULL, "Common options for all rpm modes and executables:", &common_options },
krh@215
   234
	{ OPTION_GROUP, NULL, 0, NULL, "Options implemented via popt alias/exec:", &alias_options },
krh@215
   235
	{ OPTION_GROUP, NULL, 0, NULL, "Help options", &help_options },
krh@215
   236
	{ }
krh@215
   237
};
krh@215
   238
krh@216
   239
static const char system_repo_filename[] = "system.repo";
krh@216
   240
static const char *repo_filename = system_repo_filename;
krh@216
   241
krh@216
   242
static struct razor_property *
krh@216
   243
add_property_packages(struct razor_set *set, 
krh@216
   244
		      struct razor_package_query *query,
krh@216
   245
		      const char *ref_name,
krh@216
   246
		      const char *ref_version,
krh@216
   247
		      enum razor_property_type ref_type)
krh@216
   248
{
krh@216
   249
	struct razor_property *property;
krh@216
   250
	struct razor_property_iterator *pi;
krh@216
   251
	struct razor_package_iterator *pkgi;
krh@216
   252
	const char *name, *version;
krh@216
   253
	enum razor_property_type type;
krh@216
   254
	enum razor_version_relation relation;
krh@216
   255
krh@216
   256
	pi = razor_property_iterator_create(set, NULL);
krh@216
   257
	while (razor_property_iterator_next(pi, &property, &name,
krh@216
   258
					    &relation, &version, &type)) {
krh@216
   259
		if (strcmp(ref_name, name) != 0)
krh@216
   260
			continue;
krh@216
   261
		if (ref_version && relation == RAZOR_VERSION_EQUAL &&
krh@216
   262
		    strcmp(ref_version, version) != 0)
krh@216
   263
			continue;
krh@216
   264
		if (ref_type != type)
krh@216
   265
			continue;
krh@216
   266
krh@216
   267
		pkgi = razor_package_iterator_create_for_property(set,
krh@216
   268
								  property);
krh@216
   269
		razor_package_query_add_iterator(query, pkgi);
krh@216
   270
		razor_package_iterator_destroy(pkgi);
krh@216
   271
	}
krh@216
   272
	razor_property_iterator_destroy(pi);
krh@216
   273
krh@216
   274
	return property;
krh@216
   275
}
krh@216
   276
krh@216
   277
static int
krh@216
   278
strcmpp(const void *p1, const void *p2)
krh@216
   279
{
krh@216
   280
	return strcmp(*(char * const *) p1, *(char * const *) p2);
krh@216
   281
}
krh@216
   282
krh@216
   283
static void
krh@216
   284
add_command_line_packages(struct razor_set *set,
krh@216
   285
			  struct razor_package_query *query,
krh@216
   286
			  int argc, const char **argv)
krh@216
   287
{
krh@216
   288
	struct razor_package *package;
krh@216
   289
	struct razor_package_iterator *pi;
krh@216
   290
	const char *name, *version, *arch;
krh@216
   291
	int i, cmp;
krh@216
   292
krh@216
   293
	qsort(argv, argc, sizeof(*argv), strcmpp);
krh@216
   294
	i = 0;
krh@216
   295
krh@216
   296
	pi = razor_package_iterator_create(set);
krh@216
   297
krh@216
   298
	while (razor_package_iterator_next(pi, &package,
krh@216
   299
					   &name, &version, &arch)) {
krh@216
   300
		while (cmp = strcmp(argv[i], name), cmp < 0 && i < argc) {
krh@216
   301
			printf("package %s is not installed\n", argv[i]);
krh@216
   302
			i++;
krh@216
   303
		}
krh@216
   304
krh@216
   305
		if (cmp == 0) {
krh@216
   306
			razor_package_query_add_package(query, package);
krh@216
   307
			i++;
krh@216
   308
		}
krh@216
   309
	}
krh@216
   310
krh@216
   311
	razor_package_iterator_destroy(pi);
krh@216
   312
}
krh@216
   313
krh@222
   314
static struct razor_package_iterator *
krh@222
   315
get_query_packages(struct razor_set *set, int argc, const char *argv[])
krh@222
   316
{
krh@222
   317
	struct razor_package_query *query;
krh@222
   318
	struct razor_package_iterator *pi;
krh@222
   319
	int i;
krh@222
   320
krh@222
   321
	if (option_all + option_whatprovides + option_whatrequires > 1) {
krh@222
   322
		printf("only one type of query/verify "
krh@222
   323
		       "may be performed at a time\n");
krh@222
   324
		exit(1);
krh@222
   325
	}
krh@222
   326
krh@222
   327
	query = razor_package_query_create(set);
krh@222
   328
krh@222
   329
	if (option_all) {
krh@222
   330
		pi = razor_package_iterator_create(set);
krh@222
   331
		razor_package_query_add_iterator(query, pi);
krh@222
   332
		razor_package_iterator_destroy(pi);
krh@222
   333
	} else if (option_whatrequires) {
krh@222
   334
		for (i = 0; i < argc; i++)
krh@222
   335
			add_property_packages(set, query,
krh@222
   336
					      argv[i], NULL,
krh@222
   337
					      RAZOR_PROPERTY_REQUIRES);
krh@222
   338
	} else if (option_whatprovides) {
krh@222
   339
		for (i = 0; i < argc; i++)
krh@222
   340
			add_property_packages(set, query,
krh@222
   341
					      argv[i], NULL,
krh@222
   342
					      RAZOR_PROPERTY_PROVIDES);
krh@222
   343
	} else if (argc > 0) {
krh@222
   344
		add_command_line_packages(set, query, argc, argv);
krh@222
   345
	} else {
krh@222
   346
		printf("no arguments given for query/verify\n");
krh@222
   347
		exit(1);
krh@222
   348
	}
krh@222
   349
krh@222
   350
	return razor_package_query_finish(query);
krh@222
   351
}
krh@222
   352
krh@230
   353
static const char *relation_string[] = { "<", "<=", "=", ">=", ">" };
krh@230
   354
krh@216
   355
static void
krh@216
   356
print_package_properties(struct razor_set *set,
krh@216
   357
			 struct razor_package *package,
krh@216
   358
			 enum razor_property_type ref_type)
krh@216
   359
{
krh@216
   360
	struct razor_property *property;
krh@216
   361
	struct razor_property_iterator *pi;
krh@216
   362
	const char *name, *version;
krh@216
   363
	enum razor_property_type type;
krh@216
   364
	enum razor_version_relation relation;
krh@216
   365
krh@216
   366
	pi = razor_property_iterator_create(set, package);
krh@216
   367
	while (razor_property_iterator_next(pi, &property,
krh@216
   368
					    &name, &relation, &version,
krh@216
   369
					    &type)) {
krh@216
   370
		if (type != ref_type)
krh@216
   371
			continue;
krh@216
   372
		if (version[0] == '\0')
krh@216
   373
			printf("%s\n", name);
krh@216
   374
		else
krh@216
   375
			printf("%s %s %s\n", name,
krh@230
   376
			       relation_string[relation], version);
krh@216
   377
	}
krh@216
   378
	razor_property_iterator_destroy(pi);
krh@216
   379
}
krh@216
   380
krh@218
   381
static void
krh@218
   382
print_package_info(struct razor_set *set, struct razor_package *package)
krh@218
   383
{
krh@218
   384
	printf("FIXME: Package info not tracked.\n");
krh@218
   385
}
krh@218
   386
krh@218
   387
static void
krh@218
   388
print_package_changelog(struct razor_set *set, struct razor_package *package)
krh@218
   389
{
krh@218
   390
	printf("FIXME: Package changelog not tracked.\n");
krh@218
   391
}
krh@218
   392
krh@217
   393
static struct razor_set *
krh@217
   394
create_set_from_command_line(int argc, const char *argv[])
krh@217
   395
{
krh@217
   396
	struct razor_importer *importer;
krh@217
   397
	struct razor_rpm *rpm;
krh@217
   398
	int i;
krh@217
   399
krh@217
   400
	importer = razor_importer_new();
krh@217
   401
krh@217
   402
	for (i = 0; i < argc; i++) {
krh@217
   403
		rpm = razor_rpm_open(argv[i]);
krh@217
   404
		if (rpm == NULL)
krh@217
   405
			continue;
krh@217
   406
		if (razor_importer_add_rpm(importer, rpm))
krh@217
   407
			printf("couldn't import %s\n", argv[i]);
krh@217
   408
krh@217
   409
		razor_rpm_close(rpm);
krh@217
   410
	}
krh@217
   411
krh@217
   412
	return razor_importer_finish(importer);
krh@217
   413
}
krh@217
   414
krh@215
   415
static void
krh@215
   416
command_query(int argc, const char *argv[])
krh@215
   417
{
krh@216
   418
	struct razor_set *set;
krh@216
   419
	struct razor_package_iterator *pi;
krh@216
   420
	struct razor_package *package;
krh@216
   421
	const char *name, *version, *arch;
krh@216
   422
krh@217
   423
	if (option_package) {
krh@217
   424
		set = create_set_from_command_line(argc, argv);
krh@217
   425
		argc = 0;
krh@217
   426
		option_all = 1;
krh@217
   427
	} else {
krh@217
   428
		set = razor_set_open(repo_filename);
krh@217
   429
	}
krh@216
   430
krh@222
   431
	pi = get_query_packages(set, argc, argv);
krh@216
   432
krh@216
   433
	while (razor_package_iterator_next(pi, &package,
krh@216
   434
					   &name, &version, &arch)) {
krh@216
   435
		if (option_conflicts)
krh@216
   436
			print_package_properties(set, package,
krh@216
   437
						 RAZOR_PROPERTY_CONFLICTS);
krh@216
   438
		if (option_obsoletes)
krh@216
   439
			print_package_properties(set, package,
krh@216
   440
						 RAZOR_PROPERTY_OBSOLETES);
krh@216
   441
		if (option_requires)
krh@216
   442
			print_package_properties(set, package,
krh@216
   443
						 RAZOR_PROPERTY_REQUIRES);
krh@216
   444
		if (option_provides)
krh@216
   445
			print_package_properties(set, package,
krh@216
   446
						 RAZOR_PROPERTY_PROVIDES);
krh@218
   447
		if (option_info)
krh@218
   448
			print_package_info(set, package);
krh@218
   449
		if (option_changelog)
krh@218
   450
			print_package_changelog(set, package);
krh@218
   451
krh@218
   452
		if (option_conflicts + option_obsoletes +
krh@218
   453
		    option_requires + option_provides +
krh@218
   454
		    option_info + option_changelog == 0)
krh@216
   455
			printf("%s-%s.%s\n", name, version, arch);
krh@216
   456
	}
krh@216
   457
krh@216
   458
	razor_package_iterator_destroy(pi);
krh@216
   459
krh@216
   460
	razor_set_destroy(set);
krh@216
   461
krh@216
   462
	return;
krh@215
   463
}
krh@215
   464
krh@215
   465
static void
krh@215
   466
command_verify(int argc, const char *argv[])
krh@215
   467
{
krh@222
   468
	struct razor_set *set;
krh@222
   469
	struct razor_package_iterator *pi;
krh@222
   470
	struct razor_package *package;
krh@222
   471
	const char *name, *version, *arch;
krh@222
   472
krh@222
   473
	if (option_package) {
krh@222
   474
		set = create_set_from_command_line(argc, argv);
krh@222
   475
		argc = 0;
krh@222
   476
		option_all = 1;
krh@222
   477
	} else {
krh@222
   478
		set = razor_set_open(repo_filename);
krh@215
   479
	}
krh@215
   480
krh@222
   481
	pi = get_query_packages(set, argc, argv);
krh@222
   482
krh@222
   483
	while (razor_package_iterator_next(pi, &package,
krh@222
   484
					   &name, &version, &arch)) {
krh@222
   485
		printf("verify %s-%s.%s - not implemented\n",
krh@222
   486
		       name, version, arch);
krh@222
   487
	}
krh@222
   488
krh@222
   489
	razor_package_iterator_destroy(pi);
krh@215
   490
}
krh@215
   491
krh@215
   492
static void
krh@215
   493
command_erase(int argc, const char *argv[])
krh@215
   494
{
krh@221
   495
	struct razor_set *set, *next;
krh@220
   496
	struct razor_transaction *trans;
krh@220
   497
	struct razor_package_query *query;
krh@220
   498
	struct razor_package_iterator *pi;
krh@220
   499
	struct razor_package *package;
krh@220
   500
	const char *name, *version, *arch;
krh@220
   501
krh@215
   502
	if (argc == 0) {
krh@215
   503
		printf("no packages given for erase\n");
krh@215
   504
		exit(1);
krh@215
   505
	}
krh@215
   506
krh@220
   507
	set = razor_set_open(repo_filename);
krh@220
   508
krh@220
   509
	trans = razor_transaction_create(set, NULL);
krh@220
   510
krh@220
   511
	query = razor_package_query_create(set);
krh@220
   512
	add_command_line_packages(set, query, argc, argv);
krh@221
   513
krh@220
   514
	pi = razor_package_query_finish(query);
krh@220
   515
	while (razor_package_iterator_next(pi, &package,
krh@220
   516
					   &name, &version, &arch))
krh@220
   517
		razor_transaction_remove_package(trans, package);
krh@220
   518
	razor_package_iterator_destroy(pi);
krh@220
   519
krh@221
   520
	next = razor_transaction_finish(trans);
krh@221
   521
	razor_set_destroy(set);
krh@220
   522
krh@221
   523
	razor_set_destroy(next);
krh@215
   524
}
krh@215
   525
krh@215
   526
static void
krh@215
   527
command_install(int argc, const char *argv[])
krh@215
   528
{
krh@221
   529
	struct razor_set *set, *upstream, *next;
krh@221
   530
	struct razor_transaction *trans;
krh@221
   531
	struct razor_package_iterator *pi;
krh@221
   532
	struct razor_package *package;
krh@221
   533
	const char *name, *version, *arch;
krh@221
   534
krh@215
   535
	if (argc == 0) {
krh@215
   536
		printf("no packages given for install\n");
krh@215
   537
		exit(1);
krh@215
   538
	}
krh@215
   539
krh@221
   540
	set = razor_set_open(repo_filename);
krh@221
   541
	upstream = create_set_from_command_line(argc, argv);
krh@221
   542
krh@221
   543
	trans = razor_transaction_create(set, upstream);
krh@221
   544
krh@221
   545
	pi = razor_package_iterator_create(upstream);
krh@221
   546
	while (razor_package_iterator_next(pi, &package,
krh@221
   547
					   &name, &version, &arch))
krh@221
   548
		razor_transaction_install_package(trans, package);
krh@221
   549
	razor_package_iterator_destroy(pi);
krh@221
   550
krh@221
   551
	next = razor_transaction_finish(trans);
krh@221
   552
	razor_set_destroy(set);
krh@221
   553
	razor_set_destroy(upstream);
krh@221
   554
krh@221
   555
	razor_set_destroy(next);
krh@215
   556
}
krh@215
   557
krh@215
   558
static void
krh@215
   559
command_update(int argc, const char *argv[])
krh@215
   560
{
krh@215
   561
	if (argc == 0) {
krh@215
   562
		printf("no packages given for update\n");
krh@215
   563
		exit(1);
krh@215
   564
	}
krh@215
   565
krh@215
   566
	printf("command update - not implemented\n");
krh@215
   567
}
krh@215
   568
krh@218
   569
static int
krh@219
   570
for_each_option(const struct option *options,
krh@219
   571
		const char *name, char short_name,
krh@218
   572
		void (*fn)(const struct option *o,
krh@219
   573
			   const char *name, char short_name,
krh@219
   574
			   void *data), void *data)
krh@215
   575
{
krh@218
   576
	int i, count = 0;
krh@215
   577
krh@215
   578
	for (i = 0; options[i].type != OPTION_LAST; i++) {
krh@215
   579
		switch (options[i].type) {
krh@215
   580
		case OPTION_GROUP:
krh@219
   581
			count += for_each_option(options[i].data,
krh@219
   582
						 name, short_name, fn, data);
krh@215
   583
			break;
krh@215
   584
krh@215
   585
		case OPTION_BOOL:
krh@215
   586
		case OPTION_STRING:
krh@219
   587
			if (name && strcmp(options[i].name, name) == 0) {
krh@219
   588
				fn(&options[i], name, 0, data);
krh@218
   589
				count++;
krh@219
   590
				break;
krh@218
   591
			}
krh@219
   592
krh@219
   593
			if (short_name &&
krh@219
   594
			    short_name == options[i].short_name) {
krh@219
   595
				fn(&options[i], NULL, short_name, data);
krh@218
   596
				count++;
krh@219
   597
				break;
krh@218
   598
			}
krh@215
   599
			break;
krh@215
   600
krh@215
   601
		case OPTION_LAST:
krh@215
   602
			break;
krh@215
   603
		}
krh@215
   604
	}
krh@215
   605
krh@218
   606
	return count;
krh@218
   607
}
krh@218
   608
krh@218
   609
static void
krh@219
   610
handle_option(const struct option *o,
krh@219
   611
	      const char *name, char short_name, void *data)
krh@218
   612
{
krh@218
   613
	if (o->data == NULL) {
krh@219
   614
		if (name)
krh@219
   615
			printf("option --%s not supported\n", name);
krh@219
   616
		else
krh@219
   617
			printf("option -%c not supported\n", short_name);
krh@218
   618
		return;
krh@218
   619
	}
krh@218
   620
krh@218
   621
	switch (o->type) {
krh@218
   622
	case OPTION_BOOL:
krh@218
   623
		*(int *) o->data = 1;
krh@218
   624
		break;
krh@218
   625
krh@218
   626
	case OPTION_STRING:
krh@219
   627
		*(const char **) o->data = name + strlen(o->name) + 1;
krh@218
   628
		break;
krh@218
   629
krh@218
   630
	case OPTION_LAST:
krh@218
   631
	case OPTION_GROUP:
krh@218
   632
		/* Shouldn't happen. */
krh@218
   633
		break;
krh@218
   634
	}
krh@215
   635
}
krh@215
   636
krh@215
   637
static int
krh@215
   638
parse_options(const struct option *options, int argc, const char **argv)
krh@215
   639
{
krh@219
   640
	int i, j, k;
krh@215
   641
krh@215
   642
	for (i = 1, j = 0; i < argc; i++) {
krh@215
   643
		if (argv[i][0] != '-') {
krh@215
   644
			argv[j++] = argv[i];
krh@215
   645
			continue;
krh@215
   646
		}
krh@218
   647
krh@219
   648
		if (argv[i][1] == '-') {
krh@219
   649
			if (for_each_option(options, &argv[i][2], 0,
krh@219
   650
					    handle_option, NULL) == 0) {
krh@219
   651
				printf("unknown option: %s\n", argv[i]);
krh@219
   652
				exit(1);
krh@219
   653
			}
krh@219
   654
			continue;
krh@219
   655
		}
krh@219
   656
krh@219
   657
		for (k = 1; argv[i][k]; k++) {
krh@219
   658
			if (for_each_option(options, NULL, argv[i][k],
krh@219
   659
					    handle_option, NULL) == 0) {
krh@219
   660
				printf("unknown option: -%c\n", argv[i][k]);
krh@219
   661
				exit(1);
krh@219
   662
			}
krh@215
   663
		}
krh@215
   664
	}
krh@215
   665
krh@215
   666
	return j;
krh@215
   667
}
krh@215
   668
krh@215
   669
static void
krh@215
   670
print_options_help(const struct option *options)
krh@215
   671
{
krh@215
   672
	int i;
krh@215
   673
krh@215
   674
	for (i = 0; options[i].type != OPTION_LAST; i++) {
krh@215
   675
		switch (options[i].type) {
krh@215
   676
		case OPTION_GROUP:
krh@215
   677
			printf("%s\n", options[i].description);
krh@215
   678
			print_options_help(options[i].data);
krh@215
   679
			printf("\n");
krh@215
   680
			break;
krh@215
   681
krh@215
   682
		case OPTION_BOOL:
krh@215
   683
		case OPTION_STRING:
krh@215
   684
			printf("  ");
krh@215
   685
			if (options[i].short_name)
krh@215
   686
				printf("-%c", options[i].short_name);
krh@215
   687
			if (options[i].short_name && options[i].name)
krh@215
   688
				printf(", ");
krh@215
   689
			if (options[i].name)
krh@215
   690
				printf("--%s", options[i].name);
krh@215
   691
			if (options[i].arg_name)
krh@215
   692
				printf("=%s", options[i].arg_name);
krh@215
   693
			if (options[i].description)
krh@215
   694
				printf("\t\t%s", options[i].description);
krh@215
   695
			printf("\n");
krh@215
   696
			break;
krh@215
   697
krh@215
   698
		case OPTION_LAST:
krh@215
   699
			break;
krh@215
   700
		}
krh@215
   701
	}
krh@215
   702
}
krh@215
   703
krh@215
   704
static void
krh@215
   705
print_options_usage(const struct option *options)
krh@215
   706
{
krh@215
   707
	int i;
krh@215
   708
krh@215
   709
	for (i = 0; options[i].type != OPTION_LAST; i++) {
krh@215
   710
		switch (options[i].type) {
krh@215
   711
		case OPTION_GROUP:
krh@215
   712
			print_options_usage(options[i].data);
krh@215
   713
			break;
krh@215
   714
krh@215
   715
		case OPTION_BOOL:
krh@215
   716
			printf("[");
krh@215
   717
			if (options[i].short_name)
krh@215
   718
				printf("-%c", options[i].short_name);
krh@215
   719
			if (options[i].short_name && options[i].name)
krh@215
   720
				printf("|");
krh@215
   721
			if (options[i].name)
krh@215
   722
				printf("--%s", options[i].name);
krh@215
   723
			printf("] ");
krh@215
   724
			break;
krh@215
   725
krh@215
   726
		case OPTION_STRING:
krh@215
   727
			printf("[");
krh@215
   728
			if (options[i].short_name)
krh@215
   729
				printf("-%c", options[i].short_name);
krh@215
   730
			if (options[i].short_name && options[i].name)
krh@215
   731
				printf("|");
krh@215
   732
			if (options[i].name)
krh@215
   733
				printf("--%s", options[i].name);
krh@215
   734
			if (options[i].arg_name)
krh@215
   735
				printf("=%s", options[i].arg_name);
krh@215
   736
			printf("] ");
krh@215
   737
			break;
krh@215
   738
krh@215
   739
krh@215
   740
			break;
krh@215
   741
krh@215
   742
		case OPTION_LAST:
krh@215
   743
			break;
krh@215
   744
		}
krh@215
   745
	}
krh@215
   746
}
krh@215
   747
krh@215
   748
int
krh@215
   749
main(int argc, const char *argv[])
krh@215
   750
{
krh@215
   751
	argc = parse_options(rpm_options, argc, argv);
krh@215
   752
krh@215
   753
	if (option_version) {
krh@215
   754
		printf("razor rpm version hoopla.\n");
krh@215
   755
		exit(0);
krh@215
   756
	}
krh@215
   757
krh@215
   758
	if (option_help) {
krh@215
   759
		printf("Usage: rpm [OPTION...]\n");
krh@215
   760
		print_options_help(rpm_options);
krh@215
   761
		exit(0);
krh@215
   762
	}
krh@215
   763
krh@215
   764
	if (option_usage) {
krh@215
   765
		printf("Usage: rpm [OPTION...]\n");
krh@215
   766
		print_options_usage(rpm_options);
krh@215
   767
		printf("\n");
krh@215
   768
		exit(0);
krh@215
   769
	}
krh@215
   770
krh@218
   771
	if (option_verify) {
krh@218
   772
		command_verify(argc, argv);
krh@218
   773
	} else if (option_query) {
krh@215
   774
		command_query(argc, argv);
krh@215
   775
	} else if (option_install) {
krh@215
   776
		command_install(argc, argv);
krh@215
   777
	} else if (option_upgrade) {
krh@215
   778
		command_update(argc, argv);
krh@218
   779
	} else if (option_erase) {
krh@218
   780
		command_erase(argc, argv);
krh@215
   781
	} else {
krh@215
   782
		print_options_usage(rpm_options);
krh@215
   783
		printf("\n");
krh@215
   784
		exit(0);
krh@215
   785
	}
krh@215
   786
krh@215
   787
	return 0;
krh@215
   788
}