Branch data Line data Source code
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 : 7 : 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 [ + - ]: 22 : for (t = rh->types; t->data_fn; t++)
94 [ + + ]: 22 : if (t->id == report_type)
95 : 7 : return t;
96 : :
97 : 7 : 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 : 6 : static int _copy_field(struct dm_report *rh, struct field_properties *dest,
291 : : uint32_t field_num)
292 : : {
293 : 6 : dest->field_num = field_num;
294 : 6 : dest->width = rh->fields[field_num].width;
295 : 6 : dest->flags = rh->fields[field_num].flags & DM_REPORT_FIELD_MASK;
296 : :
297 : : /* set object type method */
298 : 6 : dest->type = _find_type(rh, rh->fields[field_num].type);
299 [ - + ]: 6 : 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 : 6 : return 1;
306 : : }
307 : :
308 : 6 : 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 [ - + ]: 6 : 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 [ - + ]: 6 : 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 : 6 : 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 [ - + ]: 6 : if (fp->flags & FLD_HIDDEN)
332 : 0 : dm_list_add_h(&rh->field_props, &fp->list);
333 : : else
334 : 6 : dm_list_add(&rh->field_props, &fp->list);
335 : :
336 : 6 : 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 : 390 : 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 [ + + ][ + - ]: 390 : if (!strncasecmp(name1, name2, len2) && strlen(name1) == len2)
351 : 14 : return 1;
352 : :
353 : : /* Match including prefix? */
354 : 376 : prefix_len = strlen(prefix);
355 [ + + ][ - + ]: 376 : if (!strncasecmp(prefix, name1, prefix_len) &&
[ # # ]
356 : 72 : !strncasecmp(name1 + prefix_len, name2, len2) &&
357 : 0 : strlen(name1) == prefix_len + len2)
358 : 0 : return 1;
359 : :
360 : 390 : 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 : 12 : 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 [ - + ]: 12 : if (!flen)
419 : 0 : return 0;
420 : :
421 [ + - ]: 342 : for (f = 0; rh->fields[f].report_fn; f++)
422 [ + + ]: 342 : if (_is_same_field(rh->fields[f].id, field, flen,
423 : : rh->field_prefix)) {
424 [ + + ]: 12 : if (report_type_only) {
425 : 6 : rh->report_types |= rh->fields[f].type;
426 : 6 : return 1;
427 : : } else
428 : 6 : 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 : 12 : 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 : 2 : 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 : 2 : 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 [ + + ]: 14 : while (*we) {
517 : : /* Allow consecutive commas */
518 [ + - ][ + + ]: 22 : while (*we && *we == ',')
519 : 10 : we++;
520 : :
521 : : /* start of the field name */
522 : 12 : ws = we;
523 [ + + ][ + + ]: 94 : while (*we && *we != ',')
524 : 82 : we++;
525 : :
526 [ - + ]: 12 : 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 [ + + ]: 4 : while (*we) {
546 : : /* Allow consecutive commas */
547 [ + - ][ - + ]: 2 : while (*we && *we == ',')
548 : 0 : we++;
549 : 2 : ws = we;
550 [ + + ][ + - ]: 16 : 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 [ + - + - ]: 1 : 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 : 1 : return _output_as_columns(rh);
1094 : : }
|