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
_find_type(struct dm_report * rh,uint32_t report_type)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
dm_report_field_string(struct dm_report * rh,struct dm_report_field * field,const char ** data)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
dm_report_field_int(struct dm_report * rh,struct dm_report_field * field,const int * data)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
dm_report_field_uint32(struct dm_report * rh,struct dm_report_field * field,const uint32_t * data)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
dm_report_field_int32(struct dm_report * rh,struct dm_report_field * field,const int32_t * data)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
dm_report_field_uint64(struct dm_report * rh,struct dm_report_field * field,const uint64_t * data)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 */
dm_report_field_set_value(struct dm_report_field * field,const void * value,const void * sortvalue)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 */
_display_fields(struct dm_report * rh)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 */
_copy_field(struct dm_report * rh,struct field_properties * dest,uint32_t field_num)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
_add_field(struct dm_report * rh,uint32_t field_num,uint32_t flags)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 */
_is_same_field(const char * name1,const char * name2,size_t len2,const char * prefix)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 */
_all_match(struct dm_report * rh,const char * field,size_t flen)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 */
_add_all_fields(struct dm_report * rh,uint32_t type)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
_field_match(struct dm_report * rh,const char * field,size_t flen,unsigned report_type_only)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
_add_sort_key(struct dm_report * rh,uint32_t field_num,uint32_t flags,unsigned report_type_only)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
_key_match(struct dm_report * rh,const char * key,size_t len,unsigned report_type_only)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
_parse_fields(struct dm_report * rh,const char * format,unsigned report_type_only)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
_parse_keys(struct dm_report * rh,const char * keys,unsigned report_type_only)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
dm_report_init(uint32_t * report_types,const struct dm_report_object_type * types,const struct dm_report_field_type * fields,const char * output_fields,const char * output_separator,uint32_t output_flags,const char * sort_keys,void * private)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
dm_report_free(struct dm_report * rh)644 void dm_report_free(struct dm_report *rh)
645 {
646 dm_pool_destroy(rh->mem);
647 dm_free(rh);
648 }
649
_toupperstr(char * str)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
dm_report_set_output_field_name_prefix(struct dm_report * rh,const char * output_field_name_prefix)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 */
_report_get_field_data(struct dm_report * rh,struct field_properties * fp,void * object)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
dm_report_object(struct dm_report * rh,void * object)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 */
_report_headings(struct dm_report * rh)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 */
_row_compare(const void * a,const void * b)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
_sort_rows(struct dm_report * rh)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 */
_output_field(struct dm_report * rh,struct dm_report_field * field)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
_output_as_rows(struct dm_report * rh)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
_output_as_columns(struct dm_report * rh)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
dm_report_output(struct dm_report * rh)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