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