LCOV - code coverage report
Current view: directory - libdm - libdm-report.c (source / functions) Found Hit Coverage
Test: unnamed Lines: 509 109 21.4 %
Date: 2010-04-13 Functions: 31 12 38.7 %

       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                 : }

Generated by: LCOV version 1.7