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