src/rpm.c
author Kristian H?gsberg <krh@redhat.com>
Fri Jun 20 23:13:09 2008 -0400 (2008-06-20)
changeset 257 0c3db660514d
parent 249 061a5b815727
child 266 9b652c3617d9
permissions -rw-r--r--
When uniquifying properties, also sort them on the owning package.

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