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