Home | History | Annotate | Line # | Download | only in libdm
      1 /*	$NetBSD: libdm-report.c,v 1.1.1.3 2009/12/02 00:26:08 haad Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
      5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
      6  *
      7  * This file is part of the device-mapper userspace tools.
      8  *
      9  * This copyrighted material is made available to anyone wishing to use,
     10  * modify, copy, or redistribute it subject to the terms and conditions
     11  * of the GNU Lesser General Public License v.2.1.
     12  *
     13  * You should have received a copy of the GNU Lesser General Public License
     14  * along with this program; if not, write to the Free Software Foundation,
     15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     16  */
     17 
     18 #include "dmlib.h"
     19 
     20 #include <ctype.h>
     21 
     22 /*
     23  * Internal flags
     24  */
     25 #define RH_SORT_REQUIRED	0x00000100
     26 #define RH_HEADINGS_PRINTED	0x00000200
     27 
     28 struct dm_report {
     29 	struct dm_pool *mem;
     30 
     31 	uint32_t report_types;
     32 	const char *output_field_name_prefix;
     33 	const char *field_prefix;
     34 	uint32_t flags;
     35 	const char *separator;
     36 
     37 	uint32_t keys_count;
     38 
     39 	/* Ordered list of fields needed for this report */
     40 	struct dm_list field_props;
     41 
     42 	/* Rows of report data */
     43 	struct dm_list rows;
     44 
     45 	/* Array of field definitions */
     46 	const struct dm_report_field_type *fields;
     47 	const struct dm_report_object_type *types;
     48 
     49 	/* To store caller private data */
     50 	void *private;
     51 };
     52 
     53 /*
     54  * Internal per-field flags
     55  */
     56 #define FLD_HIDDEN	0x00000100
     57 #define FLD_SORT_KEY	0x00000200
     58 #define FLD_ASCENDING	0x00000400
     59 #define FLD_DESCENDING	0x00000800
     60 
     61 struct field_properties {
     62 	struct dm_list list;
     63 	uint32_t field_num;
     64 	uint32_t sort_posn;
     65 	int32_t width;
     66 	const struct dm_report_object_type *type;
     67 	uint32_t flags;
     68 };
     69 
     70 /*
     71  * Report data field
     72  */
     73 struct dm_report_field {
     74 	struct dm_list list;
     75 	struct field_properties *props;
     76 
     77 	const char *report_string;	/* Formatted ready for display */
     78 	const void *sort_value;		/* Raw value for sorting */
     79 };
     80 
     81 struct row {
     82 	struct dm_list list;
     83 	struct dm_report *rh;
     84 	struct dm_list fields;			  /* Fields in display order */
     85 	struct dm_report_field *(*sort_fields)[]; /* Fields in sort order */
     86 };
     87 
     88 static const struct dm_report_object_type *_find_type(struct dm_report *rh,
     89 						      uint32_t report_type)
     90 {
     91 	const struct dm_report_object_type *t;
     92 
     93 	for (t = rh->types; t->data_fn; t++)
     94 		if (t->id == report_type)
     95 			return t;
     96 
     97 	return NULL;
     98 }
     99 
    100 /*
    101  * Data-munging functions to prepare each data type for display and sorting
    102  */
    103 
    104 int dm_report_field_string(struct dm_report *rh,
    105 			   struct dm_report_field *field, const char **data)
    106 {
    107 	char *repstr;
    108 
    109 	if (!(repstr = dm_pool_strdup(rh->mem, *data))) {
    110 		log_error("dm_report_field_string: dm_pool_strdup failed");
    111 		return 0;
    112 	}
    113 
    114 	field->report_string = repstr;
    115 	field->sort_value = (const void *) field->report_string;
    116 
    117 	return 1;
    118 }
    119 
    120 int dm_report_field_int(struct dm_report *rh,
    121 			struct dm_report_field *field, const int *data)
    122 {
    123 	const int value = *data;
    124 	uint64_t *sortval;
    125 	char *repstr;
    126 
    127 	if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
    128 		log_error("dm_report_field_int: dm_pool_alloc failed");
    129 		return 0;
    130 	}
    131 
    132 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
    133 		log_error("dm_report_field_int: dm_pool_alloc failed");
    134 		return 0;
    135 	}
    136 
    137 	if (dm_snprintf(repstr, 12, "%d", value) < 0) {
    138 		log_error("dm_report_field_int: int too big: %d", value);
    139 		return 0;
    140 	}
    141 
    142 	*sortval = (const uint64_t) value;
    143 	field->sort_value = sortval;
    144 	field->report_string = repstr;
    145 
    146 	return 1;
    147 }
    148 
    149 int dm_report_field_uint32(struct dm_report *rh,
    150 			   struct dm_report_field *field, const uint32_t *data)
    151 {
    152 	const uint32_t value = *data;
    153 	uint64_t *sortval;
    154 	char *repstr;
    155 
    156 	if (!(repstr = dm_pool_zalloc(rh->mem, 12))) {
    157 		log_error("dm_report_field_uint32: dm_pool_alloc failed");
    158 		return 0;
    159 	}
    160 
    161 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
    162 		log_error("dm_report_field_uint32: dm_pool_alloc failed");
    163 		return 0;
    164 	}
    165 
    166 	if (dm_snprintf(repstr, 11, "%u", value) < 0) {
    167 		log_error("dm_report_field_uint32: uint32 too big: %u", value);
    168 		return 0;
    169 	}
    170 
    171 	*sortval = (const uint64_t) value;
    172 	field->sort_value = sortval;
    173 	field->report_string = repstr;
    174 
    175 	return 1;
    176 }
    177 
    178 int dm_report_field_int32(struct dm_report *rh,
    179 			  struct dm_report_field *field, const int32_t *data)
    180 {
    181 	const int32_t value = *data;
    182 	uint64_t *sortval;
    183 	char *repstr;
    184 
    185 	if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
    186 		log_error("dm_report_field_int32: dm_pool_alloc failed");
    187 		return 0;
    188 	}
    189 
    190 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
    191 		log_error("dm_report_field_int32: dm_pool_alloc failed");
    192 		return 0;
    193 	}
    194 
    195 	if (dm_snprintf(repstr, 12, "%d", value) < 0) {
    196 		log_error("dm_report_field_int32: int32 too big: %d", value);
    197 		return 0;
    198 	}
    199 
    200 	*sortval = (const uint64_t) value;
    201 	field->sort_value = sortval;
    202 	field->report_string = repstr;
    203 
    204 	return 1;
    205 }
    206 
    207 int dm_report_field_uint64(struct dm_report *rh,
    208 			   struct dm_report_field *field, const uint64_t *data)
    209 {
    210 	const int value = *data;
    211 	uint64_t *sortval;
    212 	char *repstr;
    213 
    214 	if (!(repstr = dm_pool_zalloc(rh->mem, 22))) {
    215 		log_error("dm_report_field_uint64: dm_pool_alloc failed");
    216 		return 0;
    217 	}
    218 
    219 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
    220 		log_error("dm_report_field_uint64: dm_pool_alloc failed");
    221 		return 0;
    222 	}
    223 
    224 	if (dm_snprintf(repstr, 21, "%d", value) < 0) {
    225 		log_error("dm_report_field_uint64: uint64 too big: %d", value);
    226 		return 0;
    227 	}
    228 
    229 	*sortval = (const uint64_t) value;
    230 	field->sort_value = sortval;
    231 	field->report_string = repstr;
    232 
    233 	return 1;
    234 }
    235 
    236 /*
    237  * Helper functions for custom report functions
    238  */
    239 void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue)
    240 {
    241 	field->report_string = (const char *) value;
    242 	field->sort_value = sortvalue ? : value;
    243 }
    244 
    245 /*
    246  * show help message
    247  */
    248 static void _display_fields(struct dm_report *rh)
    249 {
    250 	uint32_t f;
    251 	const struct dm_report_object_type *type;
    252 	const char *desc, *last_desc = "";
    253 	size_t id_len = 0;
    254 
    255 	for (f = 0; rh->fields[f].report_fn; f++)
    256 		if (strlen(rh->fields[f].id) > id_len)
    257 			id_len = strlen(rh->fields[f].id);
    258 
    259 
    260 	for (type = rh->types; type->data_fn; type++)
    261 		if (strlen(type->prefix) + 3 > id_len)
    262 			id_len = strlen(type->prefix) + 3;
    263 
    264 	for (f = 0; rh->fields[f].report_fn; f++) {
    265 		if ((type = _find_type(rh, rh->fields[f].type)) && type->desc)
    266 			desc = type->desc;
    267 		else
    268 			desc = " ";
    269 		if (desc != last_desc) {
    270 			if (*last_desc)
    271 				log_warn(" ");
    272 			log_warn("%s Fields", desc);
    273 			log_warn("%*.*s", (int) strlen(desc) + 7,
    274 				 (int) strlen(desc) + 7,
    275 				 "-------------------------------------------------------------------------------");
    276 			log_warn("  %sall%-*s - %s", type->prefix,
    277 				 (int) (id_len - 3 - strlen(type->prefix)), "",
    278 				 "All fields in this section.");
    279 		}
    280 
    281 		/* FIXME Add line-wrapping at terminal width (or 80 cols) */
    282 		log_warn("  %-*s - %s", (int) id_len, rh->fields[f].id, rh->fields[f].desc);
    283 		last_desc = desc;
    284 	}
    285 }
    286 
    287 /*
    288  * Initialise report handle
    289  */
    290 static int _copy_field(struct dm_report *rh, struct field_properties *dest,
    291 		       uint32_t field_num)
    292 {
    293 	dest->field_num = field_num;
    294 	dest->width = rh->fields[field_num].width;
    295 	dest->flags = rh->fields[field_num].flags & DM_REPORT_FIELD_MASK;
    296 
    297 	/* set object type method */
    298 	dest->type = _find_type(rh, rh->fields[field_num].type);
    299 	if (!dest->type) {
    300 		log_error("dm_report: field not match: %s",
    301 			  rh->fields[field_num].id);
    302 		return 0;
    303 	}
    304 
    305 	return 1;
    306 }
    307 
    308 static struct field_properties * _add_field(struct dm_report *rh,
    309 					    uint32_t field_num, uint32_t flags)
    310 {
    311 	struct field_properties *fp;
    312 
    313 	if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
    314 		log_error("dm_report: struct field_properties allocation "
    315 			  "failed");
    316 		return NULL;
    317 	}
    318 
    319 	if (!_copy_field(rh, fp, field_num)) {
    320 		stack;
    321 		dm_pool_free(rh->mem, fp);
    322 		return NULL;
    323 	}
    324 
    325 	fp->flags |= flags;
    326 
    327 	/*
    328 	 * Place hidden fields at the front so dm_list_end() will
    329 	 * tell us when we've reached the last visible field.
    330 	 */
    331 	if (fp->flags & FLD_HIDDEN)
    332 		dm_list_add_h(&rh->field_props, &fp->list);
    333 	else
    334 		dm_list_add(&rh->field_props, &fp->list);
    335 
    336 	return fp;
    337 }
    338 
    339 /*
    340  * Compare name1 against name2 or prefix plus name2
    341  * name2 is not necessarily null-terminated.
    342  * len2 is the length of name2.
    343  */
    344 static int _is_same_field(const char *name1, const char *name2,
    345 			  size_t len2, const char *prefix)
    346 {
    347 	size_t prefix_len;
    348 
    349 	/* Exact match? */
    350 	if (!strncasecmp(name1, name2, len2) && strlen(name1) == len2)
    351 		return 1;
    352 
    353 	/* Match including prefix? */
    354 	prefix_len = strlen(prefix);
    355 	if (!strncasecmp(prefix, name1, prefix_len) &&
    356 	    !strncasecmp(name1 + prefix_len, name2, len2) &&
    357 	    strlen(name1) == prefix_len + len2)
    358 		return 1;
    359 
    360 	return 0;
    361 }
    362 
    363 /*
    364  * Check for a report type prefix + "all" match.
    365  */
    366 static uint32_t _all_match(struct dm_report *rh, const char *field, size_t flen)
    367 {
    368 	size_t prefix_len;
    369 	const struct dm_report_object_type *t;
    370 	char prefixed_all[32];
    371 
    372 	if (!strncasecmp(field, "all", 3) && flen == 3) {
    373 		if (strlen(rh->field_prefix)) {
    374 			strcpy(prefixed_all, rh->field_prefix);
    375 			strcat(prefixed_all, "all");
    376 			/*
    377 			 * Add also prefix to receive all attributes
    378 			 * (e.g.LABEL/PVS use the same prefix)
    379 			 */
    380 			return rh->report_types |
    381 			       _all_match(rh, prefixed_all,
    382 					  strlen(prefixed_all));
    383 		} else
    384 			return rh->report_types;
    385 	}
    386 
    387 	for (t = rh->types; t->data_fn; t++) {
    388 		prefix_len = strlen(t->prefix);
    389 		if (!strncasecmp(t->prefix, field, prefix_len) &&
    390 		    !strncasecmp(field + prefix_len, "all", 3) &&
    391 		    flen == prefix_len + 3)
    392 			return t->id;
    393 	}
    394 
    395 	return 0;
    396 }
    397 
    398 /*
    399  * Add all fields with a matching type.
    400  */
    401 static int _add_all_fields(struct dm_report *rh, uint32_t type)
    402 {
    403 	uint32_t f;
    404 
    405 	for (f = 0; rh->fields[f].report_fn; f++)
    406 		if ((rh->fields[f].type & type) && !_add_field(rh, f, 0))
    407 			return 0;
    408 
    409 	return 1;
    410 }
    411 
    412 static int _field_match(struct dm_report *rh, const char *field, size_t flen,
    413 			unsigned report_type_only)
    414 {
    415 	uint32_t f, type;
    416 
    417 	if (!flen)
    418 		return 0;
    419 
    420 	for (f = 0; rh->fields[f].report_fn; f++)
    421 		if (_is_same_field(rh->fields[f].id, field, flen,
    422 				   rh->field_prefix)) {
    423 			if (report_type_only) {
    424 				rh->report_types |= rh->fields[f].type;
    425 				return 1;
    426 			} else
    427 				return _add_field(rh, f, 0) ? 1 : 0;
    428 		}
    429 
    430 	if ((type = _all_match(rh, field, flen))) {
    431 		if (report_type_only) {
    432 			rh->report_types |= type;
    433 			return 1;
    434 		} else
    435 			return  _add_all_fields(rh, type);
    436 	}
    437 
    438 	return 0;
    439 }
    440 
    441 static int _add_sort_key(struct dm_report *rh, uint32_t field_num,
    442 			 uint32_t flags, unsigned report_type_only)
    443 {
    444 	struct field_properties *fp, *found = NULL;
    445 
    446 	dm_list_iterate_items(fp, &rh->field_props) {
    447 		if (fp->field_num == field_num) {
    448 			found = fp;
    449 			break;
    450 		}
    451 	}
    452 
    453 	if (!found) {
    454 		if (report_type_only)
    455 			rh->report_types |= rh->fields[field_num].type;
    456 		else if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
    457 			return_0;
    458 	}
    459 
    460 	if (report_type_only)
    461 		return 1;
    462 
    463 	if (found->flags & FLD_SORT_KEY) {
    464 		log_error("dm_report: Ignoring duplicate sort field: %s",
    465 			  rh->fields[field_num].id);
    466 		return 1;
    467 	}
    468 
    469 	found->flags |= FLD_SORT_KEY;
    470 	found->sort_posn = rh->keys_count++;
    471 	found->flags |= flags;
    472 
    473 	return 1;
    474 }
    475 
    476 static int _key_match(struct dm_report *rh, const char *key, size_t len,
    477 		      unsigned report_type_only)
    478 {
    479 	uint32_t f;
    480 	uint32_t flags;
    481 
    482 	if (!len)
    483 		return 0;
    484 
    485 	if (*key == '+') {
    486 		key++;
    487 		len--;
    488 		flags = FLD_ASCENDING;
    489 	} else if (*key == '-') {
    490 		key++;
    491 		len--;
    492 		flags = FLD_DESCENDING;
    493 	} else
    494 		flags = FLD_ASCENDING;
    495 
    496 	if (!len) {
    497 		log_error("dm_report: Missing sort field name");
    498 		return 0;
    499 	}
    500 
    501 	for (f = 0; rh->fields[f].report_fn; f++)
    502 		if (_is_same_field(rh->fields[f].id, key, len,
    503 				   rh->field_prefix))
    504 			return _add_sort_key(rh, f, flags, report_type_only);
    505 
    506 	return 0;
    507 }
    508 
    509 static int _parse_fields(struct dm_report *rh, const char *format,
    510 			 unsigned report_type_only)
    511 {
    512 	const char *ws;		/* Word start */
    513 	const char *we = format;	/* Word end */
    514 
    515 	while (*we) {
    516 		/* Allow consecutive commas */
    517 		while (*we && *we == ',')
    518 			we++;
    519 
    520 		/* start of the field name */
    521 		ws = we;
    522 		while (*we && *we != ',')
    523 			we++;
    524 
    525 		if (!_field_match(rh, ws, (size_t) (we - ws), report_type_only)) {
    526 			_display_fields(rh);
    527 			log_warn(" ");
    528 			if (strcasecmp(ws, "help") && strcmp(ws, "?"))
    529 				log_error("Unrecognised field: %.*s",
    530 					  (int) (we - ws), ws);
    531 			return 0;
    532 		}
    533 	}
    534 
    535 	return 1;
    536 }
    537 
    538 static int _parse_keys(struct dm_report *rh, const char *keys,
    539 		       unsigned report_type_only)
    540 {
    541 	const char *ws;		/* Word start */
    542 	const char *we = keys;	/* Word end */
    543 
    544 	while (*we) {
    545 		/* Allow consecutive commas */
    546 		while (*we && *we == ',')
    547 			we++;
    548 		ws = we;
    549 		while (*we && *we != ',')
    550 			we++;
    551 		if (!_key_match(rh, ws, (size_t) (we - ws), report_type_only)) {
    552 			log_error("dm_report: Unrecognised field: %.*s",
    553 				  (int) (we - ws), ws);
    554 			return 0;
    555 		}
    556 	}
    557 
    558 	return 1;
    559 }
    560 
    561 struct dm_report *dm_report_init(uint32_t *report_types,
    562 				 const struct dm_report_object_type *types,
    563 				 const struct dm_report_field_type *fields,
    564 				 const char *output_fields,
    565 				 const char *output_separator,
    566 				 uint32_t output_flags,
    567 				 const char *sort_keys,
    568 				 void *private)
    569 {
    570 	struct dm_report *rh;
    571 	const struct dm_report_object_type *type;
    572 
    573 	if (!(rh = dm_malloc(sizeof(*rh)))) {
    574 		log_error("dm_report_init: dm_malloc failed");
    575 		return 0;
    576 	}
    577 	memset(rh, 0, sizeof(*rh));
    578 
    579 	/*
    580 	 * rh->report_types is updated in _parse_fields() and _parse_keys()
    581 	 * to contain all types corresponding to the fields specified by
    582 	 * fields or keys.
    583 	 */
    584 	if (report_types)
    585 		rh->report_types = *report_types;
    586 
    587 	rh->separator = output_separator;
    588 	rh->fields = fields;
    589 	rh->types = types;
    590 	rh->private = private;
    591 
    592 	rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
    593 
    594 	/* With columns_as_rows we must buffer and not align. */
    595 	if (output_flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS) {
    596 		if (!(output_flags & DM_REPORT_OUTPUT_BUFFERED))
    597 			rh->flags |= DM_REPORT_OUTPUT_BUFFERED;
    598 		if (output_flags & DM_REPORT_OUTPUT_ALIGNED)
    599 			rh->flags &= ~DM_REPORT_OUTPUT_ALIGNED;
    600 	}
    601 
    602 	if (output_flags & DM_REPORT_OUTPUT_BUFFERED)
    603 		rh->flags |= RH_SORT_REQUIRED;
    604 
    605 	dm_list_init(&rh->field_props);
    606 	dm_list_init(&rh->rows);
    607 
    608 	if ((type = _find_type(rh, rh->report_types)) && type->prefix)
    609 		rh->field_prefix = type->prefix;
    610 	else
    611 		rh->field_prefix = "";
    612 
    613 	if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
    614 		log_error("dm_report_init: allocation of memory pool failed");
    615 		dm_free(rh);
    616 		return NULL;
    617 	}
    618 
    619 	/*
    620 	 * To keep the code needed to add the "all" field to a minimum, we parse
    621 	 * the field lists twice.  The first time we only update the report type.
    622 	 * FIXME Use one pass instead and expand the "all" field afterwards.
    623 	 */
    624 	if (!_parse_fields(rh, output_fields, 1) ||
    625 	    !_parse_keys(rh, sort_keys, 1)) {
    626 		dm_report_free(rh);
    627 		return NULL;
    628 	}
    629 
    630 	/* Generate list of fields for output based on format string & flags */
    631 	if (!_parse_fields(rh, output_fields, 0) ||
    632 	    !_parse_keys(rh, sort_keys, 0)) {
    633 		dm_report_free(rh);
    634 		return NULL;
    635 	}
    636 
    637 	/* Return updated types value for further compatility check by caller */
    638 	if (report_types)
    639 		*report_types = rh->report_types;
    640 
    641 	return rh;
    642 }
    643 
    644 void dm_report_free(struct dm_report *rh)
    645 {
    646 	dm_pool_destroy(rh->mem);
    647 	dm_free(rh);
    648 }
    649 
    650 static char *_toupperstr(char *str)
    651 {
    652 	char *u = str;
    653 
    654 	do
    655 		*u = toupper(*u);
    656 	while (*u++);
    657 
    658 	return str;
    659 }
    660 
    661 int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
    662 {
    663 	char *prefix;
    664 
    665 	if (!(prefix = dm_pool_strdup(rh->mem, output_field_name_prefix))) {
    666 		log_error("dm_report_set_output_field_name_prefix: dm_pool_strdup failed");
    667 		return 0;
    668 	}
    669 
    670 	rh->output_field_name_prefix = _toupperstr(prefix);
    671 
    672 	return 1;
    673 }
    674 
    675 /*
    676  * Create a row of data for an object
    677  */
    678 static void * _report_get_field_data(struct dm_report *rh,
    679 			      struct field_properties *fp, void *object)
    680 {
    681 	void *ret = fp->type->data_fn(object);
    682 
    683 	if (!ret)
    684 		return NULL;
    685 
    686 	return ret + rh->fields[fp->field_num].offset;
    687 }
    688 
    689 int dm_report_object(struct dm_report *rh, void *object)
    690 {
    691 	struct field_properties *fp;
    692 	struct row *row;
    693 	struct dm_report_field *field;
    694 	void *data = NULL;
    695 
    696 	if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
    697 		log_error("dm_report_object: struct row allocation failed");
    698 		return 0;
    699 	}
    700 
    701 	row->rh = rh;
    702 
    703 	if ((rh->flags & RH_SORT_REQUIRED) &&
    704 	    !(row->sort_fields =
    705 		dm_pool_zalloc(rh->mem, sizeof(struct dm_report_field *) *
    706 			       rh->keys_count))) {
    707 		log_error("dm_report_object: "
    708 			  "row sort value structure allocation failed");
    709 		return 0;
    710 	}
    711 
    712 	dm_list_init(&row->fields);
    713 	dm_list_add(&rh->rows, &row->list);
    714 
    715 	/* For each field to be displayed, call its report_fn */
    716 	dm_list_iterate_items(fp, &rh->field_props) {
    717 		if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
    718 			log_error("dm_report_object: "
    719 				  "struct dm_report_field allocation failed");
    720 			return 0;
    721 		}
    722 		field->props = fp;
    723 
    724 		data = _report_get_field_data(rh, fp, object);
    725 		if (!data)
    726 			return 0;
    727 
    728 		if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
    729 							 field, data,
    730 							 rh->private)) {
    731 			log_error("dm_report_object: "
    732 				  "report function failed for field %s",
    733 				  rh->fields[fp->field_num].id);
    734 			return 0;
    735 		}
    736 
    737 		if ((strlen(field->report_string) > field->props->width))
    738 			field->props->width = strlen(field->report_string);
    739 
    740 		if ((rh->flags & RH_SORT_REQUIRED) &&
    741 		    (field->props->flags & FLD_SORT_KEY)) {
    742 			(*row->sort_fields)[field->props->sort_posn] = field;
    743 		}
    744 		dm_list_add(&row->fields, &field->list);
    745 	}
    746 
    747 	if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
    748 		return dm_report_output(rh);
    749 
    750 	return 1;
    751 }
    752 
    753 /*
    754  * Print row of headings
    755  */
    756 static int _report_headings(struct dm_report *rh)
    757 {
    758 	struct field_properties *fp;
    759 	const char *heading;
    760 	char buf[1024];
    761 
    762 	if (rh->flags & RH_HEADINGS_PRINTED)
    763 		return 1;
    764 
    765 	rh->flags |= RH_HEADINGS_PRINTED;
    766 
    767 	if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
    768 		return 1;
    769 
    770 	if (!dm_pool_begin_object(rh->mem, 128)) {
    771 		log_error("dm_report: "
    772 			  "dm_pool_begin_object failed for headings");
    773 		return 0;
    774 	}
    775 
    776 	/* First heading line */
    777 	dm_list_iterate_items(fp, &rh->field_props) {
    778 		if (fp->flags & FLD_HIDDEN)
    779 			continue;
    780 
    781 		heading = rh->fields[fp->field_num].heading;
    782 		if (rh->flags & DM_REPORT_OUTPUT_ALIGNED) {
    783 			if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
    784 					 fp->width, fp->width, heading) < 0) {
    785 				log_error("dm_report: snprintf heading failed");
    786 				goto bad;
    787 			}
    788 			if (!dm_pool_grow_object(rh->mem, buf, fp->width)) {
    789 				log_error("dm_report: Failed to generate report headings for printing");
    790 				goto bad;
    791 			}
    792 		} else if (!dm_pool_grow_object(rh->mem, heading, 0)) {
    793 			log_error("dm_report: Failed to generate report headings for printing");
    794 			goto bad;
    795 		}
    796 
    797 		if (!dm_list_end(&rh->field_props, &fp->list))
    798 			if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
    799 				log_error("dm_report: Failed to generate report headings for printing");
    800 				goto bad;
    801 			}
    802 	}
    803 	if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
    804 		log_error("dm_report: Failed to generate report headings for printing");
    805 		goto bad;
    806 	}
    807 	log_print("%s", (char *) dm_pool_end_object(rh->mem));
    808 
    809 	return 1;
    810 
    811       bad:
    812 	dm_pool_abandon_object(rh->mem);
    813 	return 0;
    814 }
    815 
    816 /*
    817  * Sort rows of data
    818  */
    819 static int _row_compare(const void *a, const void *b)
    820 {
    821 	const struct row *rowa = *(const struct row **) a;
    822 	const struct row *rowb = *(const struct row **) b;
    823 	const struct dm_report_field *sfa, *sfb;
    824 	uint32_t cnt;
    825 
    826 	for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
    827 		sfa = (*rowa->sort_fields)[cnt];
    828 		sfb = (*rowb->sort_fields)[cnt];
    829 		if (sfa->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) {
    830 			const uint64_t numa =
    831 			    *(const uint64_t *) sfa->sort_value;
    832 			const uint64_t numb =
    833 			    *(const uint64_t *) sfb->sort_value;
    834 
    835 			if (numa == numb)
    836 				continue;
    837 
    838 			if (sfa->props->flags & FLD_ASCENDING) {
    839 				return (numa > numb) ? 1 : -1;
    840 			} else {	/* FLD_DESCENDING */
    841 				return (numa < numb) ? 1 : -1;
    842 			}
    843 		} else {	/* DM_REPORT_FIELD_TYPE_STRING */
    844 			const char *stra = (const char *) sfa->sort_value;
    845 			const char *strb = (const char *) sfb->sort_value;
    846 			int cmp = strcmp(stra, strb);
    847 
    848 			if (!cmp)
    849 				continue;
    850 
    851 			if (sfa->props->flags & FLD_ASCENDING) {
    852 				return (cmp > 0) ? 1 : -1;
    853 			} else {	/* FLD_DESCENDING */
    854 				return (cmp < 0) ? 1 : -1;
    855 			}
    856 		}
    857 	}
    858 
    859 	return 0;		/* Identical */
    860 }
    861 
    862 static int _sort_rows(struct dm_report *rh)
    863 {
    864 	struct row *(*rows)[];
    865 	uint32_t count = 0;
    866 	struct row *row;
    867 
    868 	if (!(rows = dm_pool_alloc(rh->mem, sizeof(**rows) *
    869 				dm_list_size(&rh->rows)))) {
    870 		log_error("dm_report: sort array allocation failed");
    871 		return 0;
    872 	}
    873 
    874 	dm_list_iterate_items(row, &rh->rows)
    875 		(*rows)[count++] = row;
    876 
    877 	qsort(rows, count, sizeof(**rows), _row_compare);
    878 
    879 	dm_list_init(&rh->rows);
    880 	while (count--)
    881 		dm_list_add_h(&rh->rows, &(*rows)[count]->list);
    882 
    883 	return 1;
    884 }
    885 
    886 /*
    887  * Produce report output
    888  */
    889 static int _output_field(struct dm_report *rh, struct dm_report_field *field)
    890 {
    891 	char *field_id;
    892 	int32_t width;
    893 	uint32_t align;
    894 	const char *repstr;
    895 	char buf[4096];
    896 
    897 	if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
    898 		if (!(field_id = strdup(rh->fields[field->props->field_num].id))) {
    899 			log_error("dm_report: Failed to copy field name");
    900 			return 0;
    901 		}
    902 
    903 		if (!dm_pool_grow_object(rh->mem, rh->output_field_name_prefix, 0)) {
    904 			log_error("dm_report: Unable to extend output line");
    905 			return 0;
    906 		}
    907 
    908 		if (!dm_pool_grow_object(rh->mem, _toupperstr(field_id), 0)) {
    909 			log_error("dm_report: Unable to extend output line");
    910 			return 0;
    911 		}
    912 
    913 		free(field_id);
    914 
    915 		if (!dm_pool_grow_object(rh->mem, "=", 1)) {
    916 			log_error("dm_report: Unable to extend output line");
    917 			return 0;
    918 		}
    919 
    920 		if (!(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED) &&
    921 		    !dm_pool_grow_object(rh->mem, "\'", 1)) {
    922 			log_error("dm_report: Unable to extend output line");
    923 			return 0;
    924 		}
    925 	}
    926 
    927 	repstr = field->report_string;
    928 	width = field->props->width;
    929 	if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
    930 		if (!dm_pool_grow_object(rh->mem, repstr, 0)) {
    931 			log_error("dm_report: Unable to extend output line");
    932 			return 0;
    933 		}
    934 	} else {
    935 		if (!(align = field->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
    936 			align = (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ?
    937 				DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
    938 		if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
    939 			if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
    940 					 width, width, repstr) < 0) {
    941 				log_error("dm_report: left-aligned snprintf() failed");
    942 				return 0;
    943 			}
    944 			if (!dm_pool_grow_object(rh->mem, buf, width)) {
    945 				log_error("dm_report: Unable to extend output line");
    946 				return 0;
    947 			}
    948 		} else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
    949 			if (dm_snprintf(buf, sizeof(buf), "%*.*s",
    950 					 width, width, repstr) < 0) {
    951 				log_error("dm_report: right-aligned snprintf() failed");
    952 				return 0;
    953 			}
    954 			if (!dm_pool_grow_object(rh->mem, buf, width)) {
    955 				log_error("dm_report: Unable to extend output line");
    956 				return 0;
    957 			}
    958 		}
    959 	}
    960 
    961 	if ((rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) &&
    962 	    !(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED))
    963 		if (!dm_pool_grow_object(rh->mem, "\'", 1)) {
    964 			log_error("dm_report: Unable to extend output line");
    965 			return 0;
    966 		}
    967 
    968 	return 1;
    969 }
    970 
    971 static int _output_as_rows(struct dm_report *rh)
    972 {
    973 	struct field_properties *fp;
    974 	struct dm_report_field *field;
    975 	struct row *row;
    976 
    977 	if (!dm_pool_begin_object(rh->mem, 512)) {
    978 		log_error("dm_report: Unable to allocate output line");
    979 		return 0;
    980 	}
    981 
    982 	dm_list_iterate_items(fp, &rh->field_props) {
    983 		if (fp->flags & FLD_HIDDEN) {
    984 			dm_list_iterate_items(row, &rh->rows) {
    985 				field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field);
    986 				dm_list_del(&field->list);
    987 			}
    988 			continue;
    989 		}
    990 
    991 		if ((rh->flags & DM_REPORT_OUTPUT_HEADINGS)) {
    992 			if (!dm_pool_grow_object(rh->mem, rh->fields[fp->field_num].heading, 0)) {
    993 				log_error("dm_report: Failed to extend row for field name");
    994 				goto bad;
    995 			}
    996 			if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
    997 				log_error("dm_report: Failed to extend row with separator");
    998 				goto bad;
    999 			}
   1000 		}
   1001 
   1002 		dm_list_iterate_items(row, &rh->rows) {
   1003 			if ((field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field))) {
   1004 				if (!_output_field(rh, field))
   1005 					goto bad;
   1006 				dm_list_del(&field->list);
   1007 			}
   1008 
   1009 			if (!dm_list_end(&rh->rows, &row->list))
   1010 				if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
   1011 					log_error("dm_report: Unable to extend output line");
   1012 					goto bad;
   1013 				}
   1014 		}
   1015 
   1016 		if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
   1017 			log_error("dm_report: Failed to terminate row");
   1018 			goto bad;
   1019 		}
   1020 		log_print("%s", (char *) dm_pool_end_object(rh->mem));
   1021 	}
   1022 
   1023 	return 1;
   1024 
   1025       bad:
   1026 	dm_pool_abandon_object(rh->mem);
   1027 	return 0;
   1028 }
   1029 
   1030 static int _output_as_columns(struct dm_report *rh)
   1031 {
   1032 	struct dm_list *fh, *rowh, *ftmp, *rtmp;
   1033 	struct row *row = NULL;
   1034 	struct dm_report_field *field;
   1035 
   1036 	/* If headings not printed yet, calculate field widths and print them */
   1037 	if (!(rh->flags & RH_HEADINGS_PRINTED))
   1038 		_report_headings(rh);
   1039 
   1040 	/* Print and clear buffer */
   1041 	dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
   1042 		if (!dm_pool_begin_object(rh->mem, 512)) {
   1043 			log_error("dm_report: Unable to allocate output line");
   1044 			return 0;
   1045 		}
   1046 		row = dm_list_item(rowh, struct row);
   1047 		dm_list_iterate_safe(fh, ftmp, &row->fields) {
   1048 			field = dm_list_item(fh, struct dm_report_field);
   1049 			if (field->props->flags & FLD_HIDDEN)
   1050 				continue;
   1051 
   1052 			if (!_output_field(rh, field))
   1053 				goto bad;
   1054 
   1055 			if (!dm_list_end(&row->fields, fh))
   1056 				if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
   1057 					log_error("dm_report: Unable to extend output line");
   1058 					goto bad;
   1059 				}
   1060 
   1061 			dm_list_del(&field->list);
   1062 		}
   1063 		if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
   1064 			log_error("dm_report: Unable to terminate output line");
   1065 			goto bad;
   1066 		}
   1067 		log_print("%s", (char *) dm_pool_end_object(rh->mem));
   1068 		dm_list_del(&row->list);
   1069 	}
   1070 
   1071 	if (row)
   1072 		dm_pool_free(rh->mem, row);
   1073 
   1074 	return 1;
   1075 
   1076       bad:
   1077 	dm_pool_abandon_object(rh->mem);
   1078 	return 0;
   1079 }
   1080 
   1081 int dm_report_output(struct dm_report *rh)
   1082 {
   1083 	if (dm_list_empty(&rh->rows))
   1084 		return 1;
   1085 
   1086 	if ((rh->flags & RH_SORT_REQUIRED))
   1087 		_sort_rows(rh);
   1088 
   1089 	if ((rh->flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS))
   1090 		return _output_as_rows(rh);
   1091 	else
   1092 		return _output_as_columns(rh);
   1093 }
   1094