xref: /dragonfly/contrib/lvm2/dist/libdm/libdm-report.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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