1/*	$NetBSD$	*/
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
28struct 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
61struct 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 */
73struct 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
81struct 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
88static 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
104int 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
120int 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
149int 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
178int 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
207int 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 */
239void 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 */
248static 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 */
290static 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
308static 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 */
344static 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 */
366static 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 */
401static 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
412static 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
441static 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
476static 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
509static 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
538static 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
561struct 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
644void dm_report_free(struct dm_report *rh)
645{
646	dm_pool_destroy(rh->mem);
647	dm_free(rh);
648}
649
650static 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
661int 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 */
678static 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
689int 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 */
756static 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 */
819static 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
862static 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 */
889static 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
971static 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
1030static 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
1081int 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