Add low-level dump command to aid debugging
authorJ. Ali Harlow <ali@juiblex.co.uk>
Fri Oct 17 10:08:28 2014 +0100 (2014-10-17)
changeset 45751a084acef49
parent 456 bae5adee8c8c
child 458 3f841a46eab5
Add low-level dump command to aid debugging
librazor/Makefile.am
librazor/dump.c
librazor/razor.h.in
librazor/types/list.c
librazor/types/types.h
src/main.c
     1.1 --- a/librazor/Makefile.am	Fri Oct 17 09:57:19 2014 +0100
     1.2 +++ b/librazor/Makefile.am	Fri Oct 17 10:08:28 2014 +0100
     1.3 @@ -33,6 +33,7 @@
     1.4  	razor.h						\
     1.5  	razor.c						\
     1.6  	root.c						\
     1.7 +	dump.c						\
     1.8  	util.c						\
     1.9  	path.c						\
    1.10  	rpm.c						\
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/librazor/dump.c	Fri Oct 17 10:08:28 2014 +0100
     2.3 @@ -0,0 +1,488 @@
     2.4 +/*
     2.5 + * Copyright (C) 2014  J. Ali Harlow <ali@juiblex.co.uk>
     2.6 + *
     2.7 + * This program is free software; you can redistribute it and/or modify
     2.8 + * it under the terms of the GNU General Public License as published by
     2.9 + * the Free Software Foundation; either version 2 of the License, or
    2.10 + * (at your option) any later version.
    2.11 + *
    2.12 + * This program is distributed in the hope that it will be useful,
    2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.15 + * GNU General Public License for more details.
    2.16 + *
    2.17 + * You should have received a copy of the GNU General Public License along
    2.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    2.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    2.20 + */
    2.21 +
    2.22 +#include "config.h"
    2.23 +
    2.24 +#include <stdlib.h>
    2.25 +#include <stdio.h>
    2.26 +#include <string.h>
    2.27 +
    2.28 +#include "razor.h"
    2.29 +#include "razor-internal.h"
    2.30 +
    2.31 +static size_t
    2.32 +dump_raw(FILE *fp, void *raw, char *what, size_t length, size_t offset)
    2.33 +{
    2.34 +	size_t i, n = 0;
    2.35 +	unsigned char *bytes = raw;
    2.36 +
    2.37 +	while (length) {
    2.38 +		fprintf(fp, "%05lX %s", (unsigned long)(offset + n), what);
    2.39 +		for (i = 0; (i < 16) && (length > 0);) {
    2.40 +			if (length >= 4) {
    2.41 +				fprintf(fp, " %08X", *(uint32_t *)bytes);
    2.42 +				bytes += 4;
    2.43 +				n += 4;
    2.44 +				length -= 4;
    2.45 +				i += 4;
    2.46 +			} else if (length >= 2) {
    2.47 +				fprintf(fp, " %04X", *(unsigned short *)bytes);
    2.48 +				bytes += 2;
    2.49 +				n += 2;
    2.50 +				length -= 2;
    2.51 +				i += 2;
    2.52 +			} else {
    2.53 +				fprintf(fp, " %02X", *bytes);
    2.54 +				bytes++;
    2.55 +				n++;
    2.56 +				length--;
    2.57 +				i++;
    2.58 +			}
    2.59 +		}
    2.60 +		fprintf(fp, "\n");
    2.61 +	}
    2.62 +
    2.63 +	return n;
    2.64 +}
    2.65 +
    2.66 +static size_t
    2.67 +dump_string(FILE *fp, char *pool, size_t length, size_t offset)
    2.68 +{
    2.69 +	size_t n = 0;
    2.70 +	unsigned char c;
    2.71 +
    2.72 +	fprintf(fp, "\"");
    2.73 +	while (offset + n < length) {
    2.74 +		c = pool[offset + n++];
    2.75 +		if (c == '\0') {
    2.76 +			fprintf(fp, "\\0");
    2.77 +			break;
    2.78 +		}
    2.79 +		else if (c == '\\' || c == '"')
    2.80 +			fprintf(fp, "\\%c", c);
    2.81 +		else if (c >= ' ' && c <= '~')
    2.82 +			fprintf(fp, "%c", c);
    2.83 +		else
    2.84 +			fprintf(fp, "\\x%02X", c);
    2.85 +	}
    2.86 +	fprintf(fp, "\"");
    2.87 +
    2.88 +	return n;
    2.89 +}
    2.90 +
    2.91 +static size_t
    2.92 +dump_pool(FILE *fp, char *pool, char *what, size_t length, size_t offset)
    2.93 +{
    2.94 +	size_t n = 0;
    2.95 +
    2.96 +	fprintf(fp, "%05lX %s {\n", (unsigned long)offset, what);
    2.97 +
    2.98 +	while (n < length) {
    2.99 +		fprintf(fp, "    %08X ", (uint32_t)n);
   2.100 +		n += dump_string(fp, pool, length, n);
   2.101 +		fprintf(fp, "\n");
   2.102 +	}
   2.103 +
   2.104 +	fprintf(fp, "}\n");
   2.105 +
   2.106 +	return n;
   2.107 +}
   2.108 +
   2.109 +static size_t
   2.110 +dump_header(FILE *fp, struct razor_set_header *header, size_t length,
   2.111 +	    size_t offset)
   2.112 +{
   2.113 +	if (length >= sizeof(*header)) {
   2.114 +		fprintf(fp, "%05lX header ", (unsigned long)offset);
   2.115 +		fprintf(fp, "{ magic=%08X version=%08X num_sections=%08X }\n",
   2.116 +			header->magic, header->version, header->num_sections);
   2.117 +		return sizeof(*header);
   2.118 +	} else
   2.119 +		return dump_raw(fp, header, "unknown", length, offset);
   2.120 +}
   2.121 +
   2.122 +static size_t
   2.123 +dump_section(FILE *fp, struct razor_set_section *section, size_t length,
   2.124 +	     size_t offset, int indx)
   2.125 +{
   2.126 +	if (length >= sizeof(*section)) {
   2.127 +		fprintf(fp, "%05lX section %X ", (unsigned long)offset, indx);
   2.128 +		fprintf(fp, "{ name=%08X offset=%08X size=%08X }\n",
   2.129 +			section->name, section->offset, section->size);
   2.130 +		return sizeof(*section);
   2.131 +	} else
   2.132 +		return dump_raw(fp, section, "unknown", length, offset);
   2.133 +}
   2.134 +
   2.135 +static void
   2.136 +dump_list_head(FILE *fp, struct list_head *head)
   2.137 +{
   2.138 +	fprintf(fp, "{ list_ptr=%06X flags=%02X ",
   2.139 +		head->list_ptr, head->flags);
   2.140 +	if (head->flags == RAZOR_EMPTY_LIST)
   2.141 +		fprintf(fp, "(EMPTY_LIST) ");
   2.142 +	else if (head->flags == RAZOR_IMMEDIATE)
   2.143 +		fprintf(fp, "(IMMEDIATE) ");
   2.144 +	fprintf(fp,"}");
   2.145 +}
   2.146 +
   2.147 +static void
   2.148 +dump_list_link(FILE *fp, struct list *list)
   2.149 +{
   2.150 +	fprintf(fp, "{ data=%06X flags=%02X ",
   2.151 +		list->data, list->flags);
   2.152 +	if (list->flags == RAZOR_ENTRY_LAST)
   2.153 +		fprintf(fp, "(ENTRY_LAST) ");
   2.154 +	else if (list->flags == RAZOR_ENTRY_FIRST)
   2.155 +		fprintf(fp, "(ENTRY_FIRST) ");
   2.156 +	fprintf(fp,"}");
   2.157 +}
   2.158 +
   2.159 +static void
   2.160 +expand_pooled_string(FILE *fp, struct array *pool, uint32_t str)
   2.161 +{
   2.162 +	if (pool->data && str < pool->size) {
   2.163 +		fprintf(fp, "(");
   2.164 +		dump_string(fp, pool->data, pool->size, str);
   2.165 +		fprintf(fp, ") ");
   2.166 +	}
   2.167 +}
   2.168 +
   2.169 +static size_t
   2.170 +dump_package(FILE *fp, struct razor_package *package, size_t length,
   2.171 +	     size_t offset, struct array *string_pool, int indx)
   2.172 +{
   2.173 +	if (length >= sizeof(*package)) {
   2.174 +		fprintf(fp, "    %05lX package %X ", (unsigned long)offset,
   2.175 +			indx);
   2.176 +		fprintf(fp, "{ name=%06X ", package->name);
   2.177 +		expand_pooled_string(fp, string_pool, package->name);
   2.178 +		fprintf(fp, "flags=%02X\n            version=%08X ",
   2.179 +			package->flags, package->version);
   2.180 +		expand_pooled_string(fp, string_pool, package->version);
   2.181 +		fprintf(fp, "arch=%08X ", package->arch);
   2.182 +		expand_pooled_string(fp, string_pool, package->arch);
   2.183 +		fprintf(fp, "\n            "
   2.184 +			"summary=%08X description=%08X url=%08X license=%08X\n",
   2.185 +			package->summary, package->description,
   2.186 +			package->url, package->license);
   2.187 +		fprintf(fp,"            properties=");
   2.188 +		dump_list_head(fp, &package->properties);
   2.189 +		fprintf(fp,"\n");
   2.190 +		fprintf(fp,"            files=");
   2.191 +		dump_list_head(fp, &package->files);
   2.192 +		fprintf(fp,"\n");
   2.193 +		fprintf(fp,"            install_prefixes=");
   2.194 +		dump_list_head(fp, &package->install_prefixes);
   2.195 +		fprintf(fp,"\n");
   2.196 +		fprintf(fp,"            preun={ program=%08X body=%08X }\n",
   2.197 +		        package->preun.program, package->preun.body);
   2.198 +		fprintf(fp,"            postun={ program=%08X body=%08X }\n",
   2.199 +		        package->postun.program, package->postun.body);
   2.200 +		fprintf(fp,"    }\n");
   2.201 +		return sizeof(*package);
   2.202 +	} else
   2.203 +		return dump_raw(fp, package, "unknown", length, offset);
   2.204 +}
   2.205 +
   2.206 +static size_t
   2.207 +dump_packages(FILE *fp, struct razor_package *packages, size_t length,
   2.208 +	      size_t offset, struct array *string_pool)
   2.209 +{
   2.210 +	size_t n = 0;
   2.211 +	int i = 0;
   2.212 +
   2.213 +	fprintf(fp, "%05lX packages {\n", (unsigned long)offset);
   2.214 +
   2.215 +	while (n < length) {
   2.216 +		n += dump_package(fp, packages, length - n, n, string_pool, i);
   2.217 +		packages++;
   2.218 +		i++;
   2.219 +	}
   2.220 +
   2.221 +	fprintf(fp, "}\n");
   2.222 +
   2.223 +	return n;
   2.224 +}
   2.225 +
   2.226 +static size_t
   2.227 +dump_property(FILE *fp, struct razor_property *property, size_t length,
   2.228 +	      size_t offset, struct array *string_pool)
   2.229 +{
   2.230 +	if (length >= sizeof(*property)) {
   2.231 +		fprintf(fp, "    %05lX property ", (unsigned long)offset);
   2.232 +		fprintf(fp, "{ name=%08X ", property->name);
   2.233 +		expand_pooled_string(fp, string_pool, property->name);
   2.234 +		fprintf(fp, "\n            flags=%08X (", property->flags);
   2.235 +		if (property->flags & RAZOR_PROPERTY_LESS)
   2.236 +			fprintf(fp, "<");
   2.237 +		if (property->flags & RAZOR_PROPERTY_GREATER)
   2.238 +			fprintf(fp, ">");
   2.239 +		if (property->flags & RAZOR_PROPERTY_EQUAL)
   2.240 +			fprintf(fp, "=");
   2.241 +		switch (property->flags & RAZOR_PROPERTY_TYPE_MASK) {
   2.242 +		case RAZOR_PROPERTY_REQUIRES:
   2.243 +			fprintf(fp, " requires");
   2.244 +			break;
   2.245 +		case RAZOR_PROPERTY_PROVIDES:
   2.246 +			fprintf(fp, " provides");
   2.247 +			break;
   2.248 +		case RAZOR_PROPERTY_CONFLICTS:
   2.249 +			fprintf(fp, " conflicts");
   2.250 +			break;
   2.251 +		case RAZOR_PROPERTY_OBSOLETES:
   2.252 +			fprintf(fp, " obsoletes");
   2.253 +			break;
   2.254 +		}
   2.255 +		if (property->flags & RAZOR_PROPERTY_PRE)
   2.256 +			fprintf(fp, " pre");
   2.257 +		if (property->flags & RAZOR_PROPERTY_POST)
   2.258 +			fprintf(fp, " post");
   2.259 +		if (property->flags & RAZOR_PROPERTY_PREUN)
   2.260 +			fprintf(fp, " preun");
   2.261 +		if (property->flags & RAZOR_PROPERTY_POSTUN)
   2.262 +			fprintf(fp, " postun");
   2.263 +		fprintf(fp, ") version=%08X ", property->version);
   2.264 +		expand_pooled_string(fp, string_pool, property->version);
   2.265 +		fprintf(fp,"\n            packages=");
   2.266 +		dump_list_head(fp, &property->packages);
   2.267 +		fprintf(fp,"\n");
   2.268 +		fprintf(fp,"    }\n");
   2.269 +		return sizeof(*property);
   2.270 +	} else
   2.271 +		return dump_raw(fp, property, "unknown", length, offset);
   2.272 +}
   2.273 +
   2.274 +static size_t
   2.275 +dump_properties(FILE *fp, struct razor_property *properties, size_t length,
   2.276 +		size_t offset, struct array *string_pool)
   2.277 +{
   2.278 +	size_t n = 0;
   2.279 +
   2.280 +	fprintf(fp, "%05lX properties {\n", (unsigned long)offset);
   2.281 +
   2.282 +	while (n < length) {
   2.283 +		n += dump_property(fp, properties, length - n, n, string_pool);
   2.284 +		properties++;
   2.285 +	}
   2.286 +
   2.287 +	fprintf(fp, "}\n");
   2.288 +
   2.289 +	return n;
   2.290 +}
   2.291 +
   2.292 +static size_t
   2.293 +dump_entry(FILE *fp, struct razor_entry *entry, size_t length, size_t offset,
   2.294 +	   struct array *string_pool, int indx)
   2.295 +{
   2.296 +	if (length >= sizeof(*entry)) {
   2.297 +		fprintf(fp, "    %05lX entry %X ", (unsigned long)offset, indx);
   2.298 +		fprintf(fp, "{ name=%06X ", entry->name);
   2.299 +		expand_pooled_string(fp, string_pool, entry->name);
   2.300 +		fprintf(fp, "\n            flags=%02X ", entry->flags);
   2.301 +		if (entry->flags == RAZOR_ENTRY_LAST)
   2.302 +			fprintf(fp, "(LAST) ");
   2.303 +		fprintf(fp, "start=%08X", entry->start);
   2.304 +		if (!entry->start)
   2.305 +			fprintf(fp, " (NONE)");
   2.306 +		fprintf(fp,"\n            packages=");
   2.307 +		dump_list_head(fp, &entry->packages);
   2.308 +		fprintf(fp,"\n");
   2.309 +		fprintf(fp,"    }\n");
   2.310 +		return sizeof(*entry);
   2.311 +	} else
   2.312 +		return dump_raw(fp, entry, "unknown", length, offset);
   2.313 +}
   2.314 +
   2.315 +static size_t
   2.316 +dump_files(FILE *fp, struct razor_entry *entries, size_t length, size_t offset,
   2.317 +	   struct array *string_pool)
   2.318 +{
   2.319 +	size_t n = 0;
   2.320 +	int i = 0;
   2.321 +
   2.322 +	fprintf(fp, "%05lX files {\n", (unsigned long)offset);
   2.323 +
   2.324 +	while (n < length) {
   2.325 +		n += dump_entry(fp, entries, length - n, n, string_pool, i);
   2.326 +		i++;
   2.327 +		entries++;
   2.328 +	}
   2.329 +
   2.330 +	fprintf(fp, "}\n");
   2.331 +
   2.332 +	return n;
   2.333 +}
   2.334 +
   2.335 +static size_t
   2.336 +dump_lists(FILE *fp, struct list *lists, char *what, size_t length,
   2.337 +	   size_t offset)
   2.338 +{
   2.339 +	int i = 0;
   2.340 +	size_t n = 0;
   2.341 +
   2.342 +	fprintf(fp, "%05lX %s {\n", (unsigned long)offset, what);
   2.343 +
   2.344 +	while (n + sizeof(*lists) <= length) {
   2.345 +		fprintf(fp, "    list %06X ", i);
   2.346 +		dump_list_link(fp, lists);
   2.347 +		fprintf(fp, "\n");
   2.348 +		n += sizeof(*lists);
   2.349 +		lists++;
   2.350 +		i++;
   2.351 +	}
   2.352 +
   2.353 +	if (length - n)
   2.354 +		n += dump_raw(fp, lists, "unknown", length - n, offset + n);
   2.355 +
   2.356 +	fprintf(fp, "}\n");
   2.357 +
   2.358 +	return n;
   2.359 +}
   2.360 +
   2.361 +RAZOR_EXPORT int
   2.362 +razor_dump_database(FILE *fp, const char *root, const char *filename,
   2.363 +		    struct razor_error **error)
   2.364 +{
   2.365 +	int i;
   2.366 +	char *s, *path, *data;
   2.367 +	struct array pool = { 0, };
   2.368 +	struct array string_pool = { 0, };
   2.369 +	struct array file_string_pool = { 0, };
   2.370 +	struct array details_string_pool = { 0, };
   2.371 +	unsigned char *contents;
   2.372 +	size_t length, len, n, min_offset;
   2.373 +	struct razor_set_header *header;
   2.374 +	struct razor_set_section *section, *sections;
   2.375 +	struct razor_package *package;
   2.376 +	struct razor_property *prop;
   2.377 +	struct razor_entry *entry;
   2.378 +	struct list *list;
   2.379 +
   2.380 +	if (!filename)
   2.381 +		filename = "system.rzdb";
   2.382 +
   2.383 +	s = razor_concat(razor_get_database_path(), "/", filename, NULL);
   2.384 +	path = razor_path_add_root(s, root);
   2.385 +	free(s);
   2.386 +	contents = razor_file_get_contents(path, &length, 0, error);
   2.387 +	free(path);
   2.388 +
   2.389 +	if (!contents)
   2.390 +		return -1;
   2.391 +
   2.392 +	n = 0;
   2.393 +
   2.394 +	header = (void *)contents;
   2.395 +
   2.396 +	if (length - n)
   2.397 +		n += dump_header(fp, header, length - n, n);
   2.398 +
   2.399 +	min_offset = length;
   2.400 +	sections = (void *)(contents + n);
   2.401 +	for (i = 0; i < header->num_sections; i++) {
   2.402 +		if (n == length)
   2.403 +			break;
   2.404 +		section = (void *)(contents + n);
   2.405 +		if (section->offset < min_offset)
   2.406 +			min_offset = section->offset;
   2.407 +		n += dump_section(fp, section, length - n, n, i);
   2.408 +	}
   2.409 +
   2.410 +	if (length - n) {
   2.411 +		pool.data = (char *)(contents + n);
   2.412 +		pool.size = min_offset - n;
   2.413 +		n += dump_pool(fp, pool.data, "pool", pool.size, n);
   2.414 +	}
   2.415 +
   2.416 +	if ((length - n) && PADDING(n, 4))
   2.417 +		n += dump_raw(fp, contents + n, "padding", PADDING(n, 4), n);
   2.418 +
   2.419 +	for (i = 0; length - n && i < header->num_sections; i++) {
   2.420 +		if (sections[i].name < pool.size &&
   2.421 +		    ((char *)pool.data)[pool.size - 1] == '\0')
   2.422 +			s = pool.data + sections[i].name;
   2.423 +		else
   2.424 +			continue;
   2.425 +
   2.426 +		data = (char *)(contents + sections[i].offset);
   2.427 +
   2.428 +		if (!strcmp(s, "string_pool")) {
   2.429 +			string_pool.data = data;
   2.430 +			string_pool.size = sections[i].size;
   2.431 +		} else if (!strcmp(s, "file_string_pool")) {
   2.432 +			file_string_pool.data = data;
   2.433 +			file_string_pool.size = sections[i].size;
   2.434 +		} else if (!strcmp(s, "details_string_pool")) {
   2.435 +			details_string_pool.data = data;
   2.436 +			details_string_pool.size = sections[i].size;
   2.437 +		}
   2.438 +	}
   2.439 +
   2.440 +	for (i = 0; i < header->num_sections; i++) {
   2.441 +		if (n == length)
   2.442 +			break;
   2.443 +		section = sections + i;
   2.444 +		if (section->offset != n)
   2.445 +			continue;
   2.446 +		len = section->size;
   2.447 +		if (len > length - n)
   2.448 +			len = length - n;
   2.449 +
   2.450 +		if (section->name < pool.size &&
   2.451 +		    ((char *)pool.data)[pool.size - 1] == '\0')
   2.452 +			s = pool.data + section->name;
   2.453 +		else
   2.454 +			s = "unknown";
   2.455 +
   2.456 +		if (!strcmp(s, "string_pool") ||
   2.457 +		    !strcmp(s, "file_string_pool") ||
   2.458 +		    !strcmp(s, "details_string_pool"))
   2.459 +			n += dump_pool(fp, (char *)(contents + n), s, len, n);
   2.460 +		else if (!strcmp(s, "packages")) {
   2.461 +			package = (void *)(contents + n);
   2.462 +			n += dump_packages(fp, package, len, n, &string_pool);
   2.463 +		} else if (!strcmp(s, "properties")) {
   2.464 +			prop = (void *)(contents + n);
   2.465 +			n += dump_properties(fp, prop, len, n, &string_pool);
   2.466 +		} else if (!strcmp(s, "files")) {
   2.467 +			entry = (void *)(contents + n);
   2.468 +			n += dump_files(fp, entry, len, n, &file_string_pool);
   2.469 +		} else if (!strcmp(s, "package_pool") ||
   2.470 +			   !strcmp(s, "property_pool") ||
   2.471 +			   !strcmp(s, "prefix_pool") ||
   2.472 +			   !strcmp(s, "file_pool")) {
   2.473 +			list = (void *)(contents + n);
   2.474 +			n += dump_lists(fp, list, s, len, n);
   2.475 +		} else
   2.476 +			n += dump_raw(fp, contents + n, s, len, n);
   2.477 +
   2.478 +		if ((length - n) && PADDING(n, 4))
   2.479 +			n += dump_raw(fp, contents + n, "padding",
   2.480 +				      PADDING(n, 4), n);
   2.481 +	}
   2.482 +
   2.483 +	if (length - n)
   2.484 +		n += dump_raw(fp, contents + n, "unknown", length - n, n);
   2.485 +
   2.486 +	fprintf(fp, "%05lX EOF\n", (unsigned long)n);
   2.487 +
   2.488 +	razor_file_free_contents(contents, length);
   2.489 +
   2.490 +	return 0;
   2.491 +}
     3.1 --- a/librazor/razor.h.in	Fri Oct 17 09:57:19 2014 +0100
     3.2 +++ b/librazor/razor.h.in	Fri Oct 17 10:08:28 2014 +0100
     3.3 @@ -21,6 +21,7 @@
     3.4  #ifndef _RAZOR_H_
     3.5  #define _RAZOR_H_
     3.6  
     3.7 +#include <stdio.h>
     3.8  #include <stdint.h>
     3.9  #include <sys/types.h>
    3.10  
    3.11 @@ -631,4 +632,7 @@
    3.12  
    3.13  const char *razor_system_arch(void);
    3.14  
    3.15 +int razor_dump_database(FILE *fp, const char *root, const char *filename,
    3.16 +			struct razor_error **error);
    3.17 +
    3.18  #endif /* _RAZOR_H_ */
     4.1 --- a/librazor/types/list.c	Fri Oct 17 09:57:19 2014 +0100
     4.2 +++ b/librazor/types/list.c	Fri Oct 17 10:08:28 2014 +0100
     4.3 @@ -23,11 +23,6 @@
     4.4  
     4.5  #include "types.h"
     4.6  
     4.7 -#define RAZOR_ENTRY_LAST	0x80
     4.8 -#define RAZOR_ENTRY_FIRST	0x40
     4.9 -#define RAZOR_IMMEDIATE		(RAZOR_ENTRY_LAST | RAZOR_ENTRY_FIRST)
    4.10 -#define RAZOR_EMPTY_LIST	0xff
    4.11 -
    4.12  void
    4.13  list_set_empty(struct list_head *head)
    4.14  {
     5.1 --- a/librazor/types/types.h	Fri Oct 17 09:57:19 2014 +0100
     5.2 +++ b/librazor/types/types.h	Fri Oct 17 10:08:28 2014 +0100
     5.3 @@ -38,6 +38,11 @@
     5.4  void *array_add(struct array *array, int size);
     5.5  
     5.6  
     5.7 +#define RAZOR_ENTRY_LAST	0x80
     5.8 +#define RAZOR_ENTRY_FIRST	0x40
     5.9 +#define RAZOR_IMMEDIATE		(RAZOR_ENTRY_LAST | RAZOR_ENTRY_FIRST)
    5.10 +#define RAZOR_EMPTY_LIST	0xff
    5.11 +
    5.12  struct list_head {
    5.13  	uint32_t list_ptr : 24;
    5.14  	uint32_t flags    : 8;
     6.1 --- a/src/main.c	Fri Oct 17 09:57:19 2014 +0100
     6.2 +++ b/src/main.c	Fri Oct 17 10:08:28 2014 +0100
     6.3 @@ -1671,6 +1671,35 @@
     6.4  }
     6.5  
     6.6  static int
     6.7 +command_dump(int argc, char * const argv[])
     6.8 +{
     6.9 +	struct razor_error *error = NULL;
    6.10 +	const char *filename;
    6.11 +
    6.12 +	switch (razor_getopt(argc, argv, 0, NULL, "[filename]", NULL)) {
    6.13 +		case -2:
    6.14 +			return 0;
    6.15 +		case -1:
    6.16 +			return 1;
    6.17 +	}
    6.18 +
    6.19 +	if (argc - optind > 1) {
    6.20 +		razor_usage(argv[0], 0, NULL, "[filename]");
    6.21 +		return 1;
    6.22 +	}
    6.23 +
    6.24 +	filename = argv[optind];
    6.25 +
    6.26 +	if (razor_dump_database(stdout, install_root, filename, &error)) {
    6.27 +		fprintf(stderr, "%s\n", razor_error_get_msg(error));
    6.28 +		razor_error_free(error);
    6.29 +		return 1;
    6.30 +	}
    6.31 +
    6.32 +	return 0;
    6.33 +}
    6.34 +
    6.35 +static int
    6.36  command_info(int argc, char * const argv[])
    6.37  {
    6.38  	struct razor_error *error = NULL;
    6.39 @@ -1796,6 +1825,7 @@
    6.40  } razor_commands[] = {
    6.41  	{ "diff", "Show diff between two package sets", command_diff },
    6.42  	{ "download", "Download packages", command_download },
    6.43 +	{ "dump", "Low-level database dump (for debugging)", command_dump },
    6.44  	{ "help", "List available commands", command_help },
    6.45  #if HAVE_RPMLIB
    6.46  	{ "import-rpmdb", "Import the system rpm database",