src/rpm.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Oct 04 18:12:58 2014 +0100 (2014-10-04)
changeset 454 56ff755c268c
parent 425 0c8bdd8dc942
permissions -rw-r--r--
Only export symbols starting with razor_ in dynamic library.

Apart from being good practice to avoid clashes with higher-level
libraries and the application, this also fixes an obscure bug: The
gnulib library is used both by librazor (the dynamic library) and
by razor (the executable). In doing so, we want to have two separate
copies of the library despite the code duplication this involves.
Without the explicit limit to export only razor_ symbols, the razor
executable under mingw64 was picking up the getopt_long function
from librazor and the optind variable from libgnu which meant that
it did not see optind changing. Hiding librazor's copy of getopt
causes the linker to find libgnu's copy and everything works.

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