2007-01-16 21:04:15 +03:00
/*
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 20:26:07 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2007-01-16 21:04:15 +03:00
*
2007-08-21 20:26:07 +04:00
* This file is part of the device - mapper userspace tools .
2007-01-16 21:04:15 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 20:26:07 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2007-01-16 21:04:15 +03:00
*
2007-08-21 20:26:07 +04:00
* You should have received a copy of the GNU Lesser General Public License
2007-01-16 21:04:15 +03:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2008-11-03 21:59:59 +03:00
# include "dmlib.h"
2007-01-16 21:04:15 +03:00
2008-04-20 04:11:08 +04:00
# include <ctype.h>
2007-01-16 21:04:15 +03:00
/*
* Internal flags
*/
# define RH_SORT_REQUIRED 0x00000100
# define RH_HEADINGS_PRINTED 0x00000200
struct dm_report {
struct dm_pool * mem ;
2010-01-07 17:30:47 +03:00
/* To report all available types */
# define REPORT_TYPES_ALL UINT32_MAX
2007-01-16 21:04:15 +03:00
uint32_t report_types ;
2008-04-20 04:11:08 +04:00
const char * output_field_name_prefix ;
2007-01-16 21:04:15 +03:00
const char * field_prefix ;
uint32_t flags ;
const char * separator ;
uint32_t keys_count ;
/* Ordered list of fields needed for this report */
2008-11-04 01:14:30 +03:00
struct dm_list field_props ;
2007-01-16 21:04:15 +03:00
/* Rows of report data */
2008-11-04 01:14:30 +03:00
struct dm_list rows ;
2007-01-16 21:04:15 +03:00
/* Array of field definitions */
const struct dm_report_field_type * fields ;
const struct dm_report_object_type * types ;
/* To store caller private data */
void * private ;
} ;
/*
* Internal per - field flags
*/
# define FLD_HIDDEN 0x00000100
# define FLD_SORT_KEY 0x00000200
# define FLD_ASCENDING 0x00000400
# define FLD_DESCENDING 0x00000800
struct field_properties {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2007-01-16 21:04:15 +03:00
uint32_t field_num ;
uint32_t sort_posn ;
2007-01-29 20:23:54 +03:00
int32_t width ;
2007-01-16 21:04:15 +03:00
const struct dm_report_object_type * type ;
uint32_t flags ;
} ;
/*
* Report data field
*/
struct dm_report_field {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2007-01-16 21:04:15 +03:00
struct field_properties * props ;
const char * report_string ; /* Formatted ready for display */
const void * sort_value ; /* Raw value for sorting */
} ;
struct row {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2007-01-16 21:04:15 +03:00
struct dm_report * rh ;
2008-11-04 01:14:30 +03:00
struct dm_list fields ; /* Fields in display order */
2007-01-16 21:04:15 +03:00
struct dm_report_field * ( * sort_fields ) [ ] ; /* Fields in sort order */
} ;
static const struct dm_report_object_type * _find_type ( struct dm_report * rh ,
uint32_t report_type )
{
const struct dm_report_object_type * t ;
for ( t = rh - > types ; t - > data_fn ; t + + )
if ( t - > id = = report_type )
return t ;
return NULL ;
}
/*
* Data - munging functions to prepare each data type for display and sorting
*/
2007-01-22 18:03:57 +03:00
int dm_report_field_string ( struct dm_report * rh ,
struct dm_report_field * field , const char * * data )
2007-01-16 21:04:15 +03:00
{
char * repstr ;
2007-01-22 18:03:57 +03:00
if ( ! ( repstr = dm_pool_strdup ( rh - > mem , * data ) ) ) {
2007-01-16 21:04:15 +03:00
log_error ( " dm_report_field_string: dm_pool_strdup failed " ) ;
return 0 ;
}
field - > report_string = repstr ;
field - > sort_value = ( const void * ) field - > report_string ;
return 1 ;
}
2007-01-22 18:03:57 +03:00
int dm_report_field_int ( struct dm_report * rh ,
struct dm_report_field * field , const int * data )
2007-01-16 21:04:15 +03:00
{
2007-01-22 18:03:57 +03:00
const int value = * data ;
2007-01-16 21:04:15 +03:00
uint64_t * sortval ;
char * repstr ;
if ( ! ( repstr = dm_pool_zalloc ( rh - > mem , 13 ) ) ) {
log_error ( " dm_report_field_int: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( ! ( sortval = dm_pool_alloc ( rh - > mem , sizeof ( int64_t ) ) ) ) {
log_error ( " dm_report_field_int: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( dm_snprintf ( repstr , 12 , " %d " , value ) < 0 ) {
log_error ( " dm_report_field_int: int too big: %d " , value ) ;
return 0 ;
}
* sortval = ( const uint64_t ) value ;
field - > sort_value = sortval ;
field - > report_string = repstr ;
return 1 ;
}
2007-01-22 18:03:57 +03:00
int dm_report_field_uint32 ( struct dm_report * rh ,
struct dm_report_field * field , const uint32_t * data )
2007-01-16 21:04:15 +03:00
{
2007-01-22 18:03:57 +03:00
const uint32_t value = * data ;
2007-01-16 21:04:15 +03:00
uint64_t * sortval ;
char * repstr ;
if ( ! ( repstr = dm_pool_zalloc ( rh - > mem , 12 ) ) ) {
log_error ( " dm_report_field_uint32: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( ! ( sortval = dm_pool_alloc ( rh - > mem , sizeof ( uint64_t ) ) ) ) {
log_error ( " dm_report_field_uint32: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( dm_snprintf ( repstr , 11 , " %u " , value ) < 0 ) {
log_error ( " dm_report_field_uint32: uint32 too big: %u " , value ) ;
return 0 ;
}
* sortval = ( const uint64_t ) value ;
field - > sort_value = sortval ;
field - > report_string = repstr ;
return 1 ;
}
2007-01-22 18:03:57 +03:00
int dm_report_field_int32 ( struct dm_report * rh ,
struct dm_report_field * field , const int32_t * data )
2007-01-16 21:04:15 +03:00
{
2007-01-22 18:03:57 +03:00
const int32_t value = * data ;
2007-01-16 21:04:15 +03:00
uint64_t * sortval ;
char * repstr ;
if ( ! ( repstr = dm_pool_zalloc ( rh - > mem , 13 ) ) ) {
log_error ( " dm_report_field_int32: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( ! ( sortval = dm_pool_alloc ( rh - > mem , sizeof ( int64_t ) ) ) ) {
log_error ( " dm_report_field_int32: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( dm_snprintf ( repstr , 12 , " %d " , value ) < 0 ) {
log_error ( " dm_report_field_int32: int32 too big: %d " , value ) ;
return 0 ;
}
* sortval = ( const uint64_t ) value ;
field - > sort_value = sortval ;
field - > report_string = repstr ;
return 1 ;
}
2007-01-22 18:03:57 +03:00
int dm_report_field_uint64 ( struct dm_report * rh ,
struct dm_report_field * field , const uint64_t * data )
2007-01-16 21:04:15 +03:00
{
2010-02-15 21:36:48 +03:00
const uint64_t value = * data ;
2007-01-16 21:04:15 +03:00
uint64_t * sortval ;
char * repstr ;
if ( ! ( repstr = dm_pool_zalloc ( rh - > mem , 22 ) ) ) {
log_error ( " dm_report_field_uint64: dm_pool_alloc failed " ) ;
return 0 ;
}
if ( ! ( sortval = dm_pool_alloc ( rh - > mem , sizeof ( uint64_t ) ) ) ) {
log_error ( " dm_report_field_uint64: dm_pool_alloc failed " ) ;
return 0 ;
}
2010-02-15 21:36:48 +03:00
if ( dm_snprintf ( repstr , 21 , " % " PRIu64 , value ) < 0 ) {
log_error ( " dm_report_field_uint64: uint64 too big: % " PRIu64 , value ) ;
2007-01-16 21:04:15 +03:00
return 0 ;
}
2010-02-15 21:36:48 +03:00
* sortval = value ;
2007-01-16 21:04:15 +03:00
field - > sort_value = sortval ;
field - > report_string = repstr ;
return 1 ;
}
/*
* Helper functions for custom report functions
*/
void dm_report_field_set_value ( struct dm_report_field * field , const void * value , const void * sortvalue )
{
field - > report_string = ( const char * ) value ;
field - > sort_value = sortvalue ? : value ;
}
/*
* show help message
*/
static void _display_fields ( struct dm_report * rh )
{
uint32_t f ;
const struct dm_report_object_type * type ;
const char * desc , * last_desc = " " ;
2007-01-23 22:18:52 +03:00
size_t id_len = 0 ;
for ( f = 0 ; rh - > fields [ f ] . report_fn ; f + + )
if ( strlen ( rh - > fields [ f ] . id ) > id_len )
id_len = strlen ( rh - > fields [ f ] . id ) ;
2007-01-16 21:04:15 +03:00
2009-01-10 06:14:24 +03:00
for ( type = rh - > types ; type - > data_fn ; type + + )
if ( strlen ( type - > prefix ) + 3 > id_len )
id_len = strlen ( type - > prefix ) + 3 ;
2007-01-16 21:04:15 +03:00
for ( f = 0 ; rh - > fields [ f ] . report_fn ; f + + ) {
if ( ( type = _find_type ( rh , rh - > fields [ f ] . type ) ) & & type - > desc )
desc = type - > desc ;
else
desc = " " ;
if ( desc ! = last_desc ) {
if ( * last_desc )
2008-01-20 04:14:38 +03:00
log_warn ( " " ) ;
log_warn ( " %s Fields " , desc ) ;
log_warn ( " %*.*s " , ( int ) strlen ( desc ) + 7 ,
( int ) strlen ( desc ) + 7 ,
2008-06-25 23:52:52 +04:00
" ------------------------------------------------------------------------------- " ) ;
2009-01-10 06:14:24 +03:00
log_warn ( " %sall%-*s - %s " , type - > prefix ,
( int ) ( id_len - 3 - strlen ( type - > prefix ) ) , " " ,
" All fields in this section. " ) ;
2007-01-16 21:04:15 +03:00
}
2007-01-24 19:41:33 +03:00
/* FIXME Add line-wrapping at terminal width (or 80 cols) */
2008-01-20 04:14:38 +03:00
log_warn ( " %-*s - %s " , ( int ) id_len , rh - > fields [ f ] . id , rh - > fields [ f ] . desc ) ;
2007-01-16 21:04:15 +03:00
last_desc = desc ;
}
}
/*
* Initialise report handle
*/
static int _copy_field ( struct dm_report * rh , struct field_properties * dest ,
uint32_t field_num )
{
dest - > field_num = field_num ;
dest - > width = rh - > fields [ field_num ] . width ;
dest - > flags = rh - > fields [ field_num ] . flags & DM_REPORT_FIELD_MASK ;
/* set object type method */
dest - > type = _find_type ( rh , rh - > fields [ field_num ] . type ) ;
if ( ! dest - > type ) {
log_error ( " dm_report: field not match: %s " ,
rh - > fields [ field_num ] . id ) ;
return 0 ;
}
return 1 ;
}
2007-04-20 00:24:00 +04:00
static struct field_properties * _add_field ( struct dm_report * rh ,
uint32_t field_num , uint32_t flags )
2007-01-16 21:04:15 +03:00
{
struct field_properties * fp ;
2007-04-20 00:24:00 +04:00
if ( ! ( fp = dm_pool_zalloc ( rh - > mem , sizeof ( struct field_properties ) ) ) ) {
log_error ( " dm_report: struct field_properties allocation "
" failed " ) ;
return NULL ;
}
if ( ! _copy_field ( rh , fp , field_num ) ) {
stack ;
dm_pool_free ( rh - > mem , fp ) ;
return NULL ;
}
fp - > flags | = flags ;
2007-04-27 19:22:27 +04:00
/*
2008-11-04 01:14:30 +03:00
* Place hidden fields at the front so dm_list_end ( ) will
2007-04-27 19:22:27 +04:00
* tell us when we ' ve reached the last visible field .
*/
if ( fp - > flags & FLD_HIDDEN )
2008-11-04 01:14:30 +03:00
dm_list_add_h ( & rh - > field_props , & fp - > list ) ;
2007-04-27 19:22:27 +04:00
else
2008-11-04 01:14:30 +03:00
dm_list_add ( & rh - > field_props , & fp - > list ) ;
2007-04-20 00:24:00 +04:00
return fp ;
}
/*
* Compare name1 against name2 or prefix plus name2
* name2 is not necessarily null - terminated .
* len2 is the length of name2 .
*/
static int _is_same_field ( const char * name1 , const char * name2 ,
size_t len2 , const char * prefix )
{
size_t prefix_len ;
/* Exact match? */
if ( ! strncasecmp ( name1 , name2 , len2 ) & & strlen ( name1 ) = = len2 )
return 1 ;
/* Match including prefix? */
prefix_len = strlen ( prefix ) ;
if ( ! strncasecmp ( prefix , name1 , prefix_len ) & &
! strncasecmp ( name1 + prefix_len , name2 , len2 ) & &
strlen ( name1 ) = = prefix_len + len2 )
return 1 ;
return 0 ;
}
2009-01-10 06:01:35 +03:00
/*
* Check for a report type prefix + " all " match .
*/
static uint32_t _all_match ( struct dm_report * rh , const char * field , size_t flen )
{
size_t prefix_len ;
const struct dm_report_object_type * t ;
2009-02-09 12:45:49 +03:00
char prefixed_all [ 32 ] ;
if ( ! strncasecmp ( field , " all " , 3 ) & & flen = = 3 ) {
if ( strlen ( rh - > field_prefix ) ) {
strcpy ( prefixed_all , rh - > field_prefix ) ;
strcat ( prefixed_all , " all " ) ;
/*
* Add also prefix to receive all attributes
* ( e . g . LABEL / PVS use the same prefix )
*/
return rh - > report_types |
_all_match ( rh , prefixed_all ,
strlen ( prefixed_all ) ) ;
} else
2010-01-07 17:30:47 +03:00
return ( rh - > report_types )
? rh - > report_types : REPORT_TYPES_ALL ;
2009-02-09 12:45:49 +03:00
}
2009-01-10 06:01:35 +03:00
for ( t = rh - > types ; t - > data_fn ; t + + ) {
prefix_len = strlen ( t - > prefix ) ;
if ( ! strncasecmp ( t - > prefix , field , prefix_len ) & &
! strncasecmp ( field + prefix_len , " all " , 3 ) & &
flen = = prefix_len + 3 )
return t - > id ;
}
return 0 ;
}
/*
* Add all fields with a matching type .
*/
static int _add_all_fields ( struct dm_report * rh , uint32_t type )
2007-04-20 00:24:00 +04:00
{
uint32_t f ;
2009-01-10 06:01:35 +03:00
for ( f = 0 ; rh - > fields [ f ] . report_fn ; f + + )
if ( ( rh - > fields [ f ] . type & type ) & & ! _add_field ( rh , f , 0 ) )
return 0 ;
return 1 ;
}
static int _field_match ( struct dm_report * rh , const char * field , size_t flen ,
unsigned report_type_only )
{
uint32_t f , type ;
2007-01-16 21:04:15 +03:00
if ( ! flen )
return 0 ;
2007-04-20 00:24:00 +04:00
for ( f = 0 ; rh - > fields [ f ] . report_fn ; f + + )
if ( _is_same_field ( rh - > fields [ f ] . id , field , flen ,
2009-01-10 06:01:35 +03:00
rh - > field_prefix ) ) {
if ( report_type_only ) {
rh - > report_types | = rh - > fields [ f ] . type ;
return 1 ;
} else
return _add_field ( rh , f , 0 ) ? 1 : 0 ;
}
if ( ( type = _all_match ( rh , field , flen ) ) ) {
if ( report_type_only ) {
rh - > report_types | = type ;
return 1 ;
} else
return _add_all_fields ( rh , type ) ;
}
2007-01-16 21:04:15 +03:00
return 0 ;
}
static int _add_sort_key ( struct dm_report * rh , uint32_t field_num ,
2009-01-10 06:01:35 +03:00
uint32_t flags , unsigned report_type_only )
2007-01-16 21:04:15 +03:00
{
struct field_properties * fp , * found = NULL ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( fp , & rh - > field_props ) {
2007-01-16 21:04:15 +03:00
if ( fp - > field_num = = field_num ) {
found = fp ;
break ;
}
}
2009-01-10 06:01:35 +03:00
if ( ! found ) {
if ( report_type_only )
rh - > report_types | = rh - > fields [ field_num ] . type ;
else if ( ! ( found = _add_field ( rh , field_num , FLD_HIDDEN ) ) )
return_0 ;
}
if ( report_type_only )
return 1 ;
2007-01-16 21:04:15 +03:00
if ( found - > flags & FLD_SORT_KEY ) {
log_error ( " dm_report: Ignoring duplicate sort field: %s " ,
rh - > fields [ field_num ] . id ) ;
return 1 ;
}
found - > flags | = FLD_SORT_KEY ;
found - > sort_posn = rh - > keys_count + + ;
found - > flags | = flags ;
return 1 ;
}
2009-01-10 06:01:35 +03:00
static int _key_match ( struct dm_report * rh , const char * key , size_t len ,
unsigned report_type_only )
2007-01-16 21:04:15 +03:00
{
2007-04-20 00:24:00 +04:00
uint32_t f ;
2007-01-16 21:04:15 +03:00
uint32_t flags ;
if ( ! len )
return 0 ;
if ( * key = = ' + ' ) {
key + + ;
len - - ;
flags = FLD_ASCENDING ;
} else if ( * key = = ' - ' ) {
key + + ;
len - - ;
flags = FLD_DESCENDING ;
} else
flags = FLD_ASCENDING ;
if ( ! len ) {
log_error ( " dm_report: Missing sort field name " ) ;
return 0 ;
}
2007-04-20 00:24:00 +04:00
for ( f = 0 ; rh - > fields [ f ] . report_fn ; f + + )
if ( _is_same_field ( rh - > fields [ f ] . id , key , len ,
rh - > field_prefix ) )
2009-01-10 06:01:35 +03:00
return _add_sort_key ( rh , f , flags , report_type_only ) ;
2007-01-16 21:04:15 +03:00
return 0 ;
}
2009-01-19 23:53:35 +03:00
static int _parse_fields ( struct dm_report * rh , const char * format ,
unsigned report_type_only )
2007-01-16 21:04:15 +03:00
{
const char * ws ; /* Word start */
const char * we = format ; /* Word end */
while ( * we ) {
/* Allow consecutive commas */
while ( * we & & * we = = ' , ' )
we + + ;
/* start of the field name */
ws = we ;
while ( * we & & * we ! = ' , ' )
we + + ;
2009-01-10 06:01:35 +03:00
if ( ! _field_match ( rh , ws , ( size_t ) ( we - ws ) , report_type_only ) ) {
2007-01-16 21:04:15 +03:00
_display_fields ( rh ) ;
2008-01-20 04:14:38 +03:00
log_warn ( " " ) ;
2007-01-19 01:15:04 +03:00
if ( strcasecmp ( ws , " help " ) & & strcmp ( ws , " ? " ) )
log_error ( " Unrecognised field: %.*s " ,
( int ) ( we - ws ) , ws ) ;
2007-01-16 21:04:15 +03:00
return 0 ;
}
}
return 1 ;
}
2009-01-10 06:01:35 +03:00
static int _parse_keys ( struct dm_report * rh , const char * keys ,
unsigned report_type_only )
2007-01-16 21:04:15 +03:00
{
const char * ws ; /* Word start */
const char * we = keys ; /* Word end */
while ( * we ) {
/* Allow consecutive commas */
while ( * we & & * we = = ' , ' )
we + + ;
ws = we ;
while ( * we & & * we ! = ' , ' )
we + + ;
2009-01-10 06:01:35 +03:00
if ( ! _key_match ( rh , ws , ( size_t ) ( we - ws ) , report_type_only ) ) {
2010-01-12 00:28:04 +03:00
log_error ( " dm_report: Unrecognised field: %.*s " ,
2007-01-16 21:04:15 +03:00
( int ) ( we - ws ) , ws ) ;
return 0 ;
}
}
return 1 ;
}
struct dm_report * dm_report_init ( uint32_t * report_types ,
const struct dm_report_object_type * types ,
const struct dm_report_field_type * fields ,
const char * output_fields ,
const char * output_separator ,
uint32_t output_flags ,
const char * sort_keys ,
void * private )
{
struct dm_report * rh ;
const struct dm_report_object_type * type ;
if ( ! ( rh = dm_malloc ( sizeof ( * rh ) ) ) ) {
log_error ( " dm_report_init: dm_malloc failed " ) ;
return 0 ;
}
memset ( rh , 0 , sizeof ( * rh ) ) ;
/*
2009-01-19 23:53:35 +03:00
* rh - > report_types is updated in _parse_fields ( ) and _parse_keys ( )
2007-01-16 21:04:15 +03:00
* to contain all types corresponding to the fields specified by
2009-01-19 23:53:35 +03:00
* fields or keys .
2007-01-16 21:04:15 +03:00
*/
if ( report_types )
rh - > report_types = * report_types ;
rh - > separator = output_separator ;
rh - > fields = fields ;
rh - > types = types ;
rh - > private = private ;
rh - > flags | = output_flags & DM_REPORT_OUTPUT_MASK ;
2008-06-25 02:53:48 +04:00
/* With columns_as_rows we must buffer and not align. */
if ( output_flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS ) {
if ( ! ( output_flags & DM_REPORT_OUTPUT_BUFFERED ) )
rh - > flags | = DM_REPORT_OUTPUT_BUFFERED ;
if ( output_flags & DM_REPORT_OUTPUT_ALIGNED )
rh - > flags & = ~ DM_REPORT_OUTPUT_ALIGNED ;
}
2007-01-16 21:04:15 +03:00
if ( output_flags & DM_REPORT_OUTPUT_BUFFERED )
rh - > flags | = RH_SORT_REQUIRED ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & rh - > field_props ) ;
dm_list_init ( & rh - > rows ) ;
2007-01-16 21:04:15 +03:00
if ( ( type = _find_type ( rh , rh - > report_types ) ) & & type - > prefix )
rh - > field_prefix = type - > prefix ;
else
rh - > field_prefix = " " ;
if ( ! ( rh - > mem = dm_pool_create ( " report " , 10 * 1024 ) ) ) {
log_error ( " dm_report_init: allocation of memory pool failed " ) ;
2007-02-14 18:12:16 +03:00
dm_free ( rh ) ;
2007-01-16 21:04:15 +03:00
return NULL ;
}
2009-01-10 06:01:35 +03:00
/*
* To keep the code needed to add the " all " field to a minimum , we parse
* the field lists twice . The first time we only update the report type .
* FIXME Use one pass instead and expand the " all " field afterwards .
*/
2009-01-19 23:53:35 +03:00
if ( ! _parse_fields ( rh , output_fields , 1 ) | |
2009-01-10 06:01:35 +03:00
! _parse_keys ( rh , sort_keys , 1 ) ) {
2007-02-14 18:12:16 +03:00
dm_report_free ( rh ) ;
2007-01-16 21:04:15 +03:00
return NULL ;
2007-02-14 18:12:16 +03:00
}
2007-01-16 21:04:15 +03:00
2009-01-10 06:01:35 +03:00
/* Generate list of fields for output based on format string & flags */
2009-01-19 23:53:35 +03:00
if ( ! _parse_fields ( rh , output_fields , 0 ) | |
2009-01-10 06:01:35 +03:00
! _parse_keys ( rh , sort_keys , 0 ) ) {
2007-02-14 18:12:16 +03:00
dm_report_free ( rh ) ;
2007-01-16 21:04:15 +03:00
return NULL ;
2007-02-14 18:12:16 +03:00
}
2007-01-16 21:04:15 +03:00
/* Return updated types value for further compatility check by caller */
if ( report_types )
* report_types = rh - > report_types ;
return rh ;
}
void dm_report_free ( struct dm_report * rh )
{
dm_pool_destroy ( rh - > mem ) ;
dm_free ( rh ) ;
}
2008-04-20 04:11:08 +04:00
static char * _toupperstr ( char * str )
{
char * u = str ;
do
* u = toupper ( * u ) ;
while ( * u + + ) ;
return str ;
}
int dm_report_set_output_field_name_prefix ( struct dm_report * rh , const char * output_field_name_prefix )
{
char * prefix ;
if ( ! ( prefix = dm_pool_strdup ( rh - > mem , output_field_name_prefix ) ) ) {
log_error ( " dm_report_set_output_field_name_prefix: dm_pool_strdup failed " ) ;
return 0 ;
}
rh - > output_field_name_prefix = _toupperstr ( prefix ) ;
return 1 ;
}
2007-01-16 21:04:15 +03:00
/*
* Create a row of data for an object
*/
static void * _report_get_field_data ( struct dm_report * rh ,
struct field_properties * fp , void * object )
{
void * ret = fp - > type - > data_fn ( object ) ;
if ( ! ret )
return NULL ;
return ret + rh - > fields [ fp - > field_num ] . offset ;
}
int dm_report_object ( struct dm_report * rh , void * object )
{
struct field_properties * fp ;
struct row * row ;
struct dm_report_field * field ;
void * data = NULL ;
if ( ! ( row = dm_pool_zalloc ( rh - > mem , sizeof ( * row ) ) ) ) {
log_error ( " dm_report_object: struct row allocation failed " ) ;
return 0 ;
}
row - > rh = rh ;
if ( ( rh - > flags & RH_SORT_REQUIRED ) & &
! ( row - > sort_fields =
dm_pool_zalloc ( rh - > mem , sizeof ( struct dm_report_field * ) *
rh - > keys_count ) ) ) {
log_error ( " dm_report_object: "
" row sort value structure allocation failed " ) ;
return 0 ;
}
2008-11-04 01:14:30 +03:00
dm_list_init ( & row - > fields ) ;
dm_list_add ( & rh - > rows , & row - > list ) ;
2007-01-16 21:04:15 +03:00
/* For each field to be displayed, call its report_fn */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( fp , & rh - > field_props ) {
2007-01-16 21:04:15 +03:00
if ( ! ( field = dm_pool_zalloc ( rh - > mem , sizeof ( * field ) ) ) ) {
log_error ( " dm_report_object: "
" struct dm_report_field allocation failed " ) ;
return 0 ;
}
field - > props = fp ;
data = _report_get_field_data ( rh , fp , object ) ;
if ( ! data )
return 0 ;
if ( ! rh - > fields [ fp - > field_num ] . report_fn ( rh , rh - > mem ,
field , data ,
rh - > private ) ) {
log_error ( " dm_report_object: "
" report function failed for field %s " ,
rh - > fields [ fp - > field_num ] . id ) ;
return 0 ;
}
if ( ( strlen ( field - > report_string ) > field - > props - > width ) )
field - > props - > width = strlen ( field - > report_string ) ;
if ( ( rh - > flags & RH_SORT_REQUIRED ) & &
( field - > props - > flags & FLD_SORT_KEY ) ) {
( * row - > sort_fields ) [ field - > props - > sort_posn ] = field ;
}
2008-11-04 01:14:30 +03:00
dm_list_add ( & row - > fields , & field - > list ) ;
2007-01-16 21:04:15 +03:00
}
if ( ! ( rh - > flags & DM_REPORT_OUTPUT_BUFFERED ) )
return dm_report_output ( rh ) ;
return 1 ;
}
/*
* Print row of headings
*/
static int _report_headings ( struct dm_report * rh )
{
struct field_properties * fp ;
const char * heading ;
char buf [ 1024 ] ;
if ( rh - > flags & RH_HEADINGS_PRINTED )
return 1 ;
rh - > flags | = RH_HEADINGS_PRINTED ;
if ( ! ( rh - > flags & DM_REPORT_OUTPUT_HEADINGS ) )
return 1 ;
if ( ! dm_pool_begin_object ( rh - > mem , 128 ) ) {
log_error ( " dm_report: "
" dm_pool_begin_object failed for headings " ) ;
return 0 ;
}
/* First heading line */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( fp , & rh - > field_props ) {
2007-01-16 21:04:15 +03:00
if ( fp - > flags & FLD_HIDDEN )
continue ;
heading = rh - > fields [ fp - > field_num ] . heading ;
if ( rh - > flags & DM_REPORT_OUTPUT_ALIGNED ) {
if ( dm_snprintf ( buf , sizeof ( buf ) , " %-*.*s " ,
fp - > width , fp - > width , heading ) < 0 ) {
log_error ( " dm_report: snprintf heading failed " ) ;
goto bad ;
}
if ( ! dm_pool_grow_object ( rh - > mem , buf , fp - > width ) ) {
log_error ( " dm_report: Failed to generate report headings for printing " ) ;
goto bad ;
}
2008-04-19 19:50:18 +04:00
} else if ( ! dm_pool_grow_object ( rh - > mem , heading , 0 ) ) {
2007-01-16 21:04:15 +03:00
log_error ( " dm_report: Failed to generate report headings for printing " ) ;
goto bad ;
}
2008-11-04 01:14:30 +03:00
if ( ! dm_list_end ( & rh - > field_props , & fp - > list ) )
2008-04-19 19:50:18 +04:00
if ( ! dm_pool_grow_object ( rh - > mem , rh - > separator , 0 ) ) {
2007-01-16 21:04:15 +03:00
log_error ( " dm_report: Failed to generate report headings for printing " ) ;
goto bad ;
}
}
if ( ! dm_pool_grow_object ( rh - > mem , " \0 " , 1 ) ) {
log_error ( " dm_report: Failed to generate report headings for printing " ) ;
goto bad ;
}
log_print ( " %s " , ( char * ) dm_pool_end_object ( rh - > mem ) ) ;
return 1 ;
bad :
dm_pool_abandon_object ( rh - > mem ) ;
return 0 ;
}
/*
* Sort rows of data
*/
static int _row_compare ( const void * a , const void * b )
{
2010-02-15 21:34:00 +03:00
const struct row * rowa = * ( const struct row * const * ) a ;
const struct row * rowb = * ( const struct row * const * ) b ;
2007-01-16 21:04:15 +03:00
const struct dm_report_field * sfa , * sfb ;
2007-01-17 00:13:07 +03:00
uint32_t cnt ;
2007-01-16 21:04:15 +03:00
for ( cnt = 0 ; cnt < rowa - > rh - > keys_count ; cnt + + ) {
sfa = ( * rowa - > sort_fields ) [ cnt ] ;
sfb = ( * rowb - > sort_fields ) [ cnt ] ;
2007-01-18 20:47:58 +03:00
if ( sfa - > props - > flags & DM_REPORT_FIELD_TYPE_NUMBER ) {
2007-01-16 21:04:15 +03:00
const uint64_t numa =
* ( const uint64_t * ) sfa - > sort_value ;
const uint64_t numb =
* ( const uint64_t * ) sfb - > sort_value ;
if ( numa = = numb )
continue ;
if ( sfa - > props - > flags & FLD_ASCENDING ) {
return ( numa > numb ) ? 1 : - 1 ;
} else { /* FLD_DESCENDING */
return ( numa < numb ) ? 1 : - 1 ;
}
2007-01-18 20:47:58 +03:00
} else { /* DM_REPORT_FIELD_TYPE_STRING */
2007-01-16 21:04:15 +03:00
const char * stra = ( const char * ) sfa - > sort_value ;
const char * strb = ( const char * ) sfb - > sort_value ;
int cmp = strcmp ( stra , strb ) ;
if ( ! cmp )
continue ;
if ( sfa - > props - > flags & FLD_ASCENDING ) {
return ( cmp > 0 ) ? 1 : - 1 ;
} else { /* FLD_DESCENDING */
return ( cmp < 0 ) ? 1 : - 1 ;
}
}
}
return 0 ; /* Identical */
}
static int _sort_rows ( struct dm_report * rh )
{
struct row * ( * rows ) [ ] ;
uint32_t count = 0 ;
struct row * row ;
if ( ! ( rows = dm_pool_alloc ( rh - > mem , sizeof ( * * rows ) *
2008-11-04 01:14:30 +03:00
dm_list_size ( & rh - > rows ) ) ) ) {
2007-01-16 21:04:15 +03:00
log_error ( " dm_report: sort array allocation failed " ) ;
return 0 ;
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( row , & rh - > rows )
2007-01-16 21:04:15 +03:00
( * rows ) [ count + + ] = row ;
qsort ( rows , count , sizeof ( * * rows ) , _row_compare ) ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & rh - > rows ) ;
2007-01-16 21:04:15 +03:00
while ( count - - )
2008-11-04 01:14:30 +03:00
dm_list_add_h ( & rh - > rows , & ( * rows ) [ count ] - > list ) ;
2007-01-16 21:04:15 +03:00
return 1 ;
}
/*
* Produce report output
*/
2008-06-25 02:53:48 +04:00
static int _output_field ( struct dm_report * rh , struct dm_report_field * field )
2007-01-16 21:04:15 +03:00
{
2008-04-20 04:11:08 +04:00
char * field_id ;
2007-01-29 20:23:54 +03:00
int32_t width ;
2007-01-18 20:47:58 +03:00
uint32_t align ;
2008-06-25 02:53:48 +04:00
const char * repstr ;
char buf [ 4096 ] ;
2007-01-16 21:04:15 +03:00
2008-06-25 02:53:48 +04:00
if ( rh - > flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX ) {
if ( ! ( field_id = strdup ( rh - > fields [ field - > props - > field_num ] . id ) ) ) {
log_error ( " dm_report: Failed to copy field name " ) ;
return 0 ;
}
2007-01-16 21:04:15 +03:00
2008-06-25 02:53:48 +04:00
if ( ! dm_pool_grow_object ( rh - > mem , rh - > output_field_name_prefix , 0 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
if ( ! dm_pool_grow_object ( rh - > mem , _toupperstr ( field_id ) , 0 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
free ( field_id ) ;
if ( ! dm_pool_grow_object ( rh - > mem , " = " , 1 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
if ( ! ( rh - > flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED ) & &
! dm_pool_grow_object ( rh - > mem , " \' " , 1 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
}
repstr = field - > report_string ;
width = field - > props - > width ;
if ( ! ( rh - > flags & DM_REPORT_OUTPUT_ALIGNED ) ) {
if ( ! dm_pool_grow_object ( rh - > mem , repstr , 0 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
} else {
if ( ! ( align = field - > props - > flags & DM_REPORT_FIELD_ALIGN_MASK ) )
align = ( field - > props - > flags & DM_REPORT_FIELD_TYPE_NUMBER ) ?
DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT ;
if ( align & DM_REPORT_FIELD_ALIGN_LEFT ) {
if ( dm_snprintf ( buf , sizeof ( buf ) , " %-*.*s " ,
width , width , repstr ) < 0 ) {
log_error ( " dm_report: left-aligned snprintf() failed " ) ;
return 0 ;
}
if ( ! dm_pool_grow_object ( rh - > mem , buf , width ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
} else if ( align & DM_REPORT_FIELD_ALIGN_RIGHT ) {
if ( dm_snprintf ( buf , sizeof ( buf ) , " %*.*s " ,
width , width , repstr ) < 0 ) {
log_error ( " dm_report: right-aligned snprintf() failed " ) ;
return 0 ;
}
if ( ! dm_pool_grow_object ( rh - > mem , buf , width ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
}
}
if ( ( rh - > flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX ) & &
! ( rh - > flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED ) )
if ( ! dm_pool_grow_object ( rh - > mem , " \' " , 1 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
return 0 ;
}
return 1 ;
}
static int _output_as_rows ( struct dm_report * rh )
{
struct field_properties * fp ;
struct dm_report_field * field ;
struct row * row ;
if ( ! dm_pool_begin_object ( rh - > mem , 512 ) ) {
log_error ( " dm_report: Unable to allocate output line " ) ;
return 0 ;
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( fp , & rh - > field_props ) {
2008-06-25 04:10:36 +04:00
if ( fp - > flags & FLD_HIDDEN ) {
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( row , & rh - > rows ) {
field = dm_list_item ( dm_list_first ( & row - > fields ) , struct dm_report_field ) ;
dm_list_del ( & field - > list ) ;
2008-06-25 04:10:36 +04:00
}
2008-06-25 02:53:48 +04:00
continue ;
2008-06-25 04:10:36 +04:00
}
2008-06-25 02:53:48 +04:00
if ( ( rh - > flags & DM_REPORT_OUTPUT_HEADINGS ) ) {
if ( ! dm_pool_grow_object ( rh - > mem , rh - > fields [ fp - > field_num ] . heading , 0 ) ) {
log_error ( " dm_report: Failed to extend row for field name " ) ;
goto bad ;
}
if ( ! dm_pool_grow_object ( rh - > mem , rh - > separator , 0 ) ) {
log_error ( " dm_report: Failed to extend row with separator " ) ;
goto bad ;
}
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( row , & rh - > rows ) {
if ( ( field = dm_list_item ( dm_list_first ( & row - > fields ) , struct dm_report_field ) ) ) {
2008-06-25 04:10:36 +04:00
if ( ! _output_field ( rh , field ) )
goto bad ;
2008-11-04 01:14:30 +03:00
dm_list_del ( & field - > list ) ;
2008-06-25 04:10:36 +04:00
}
2008-06-25 02:53:48 +04:00
2008-11-04 01:14:30 +03:00
if ( ! dm_list_end ( & rh - > rows , & row - > list ) )
2008-06-25 02:53:48 +04:00
if ( ! dm_pool_grow_object ( rh - > mem , rh - > separator , 0 ) ) {
log_error ( " dm_report: Unable to extend output line " ) ;
goto bad ;
}
}
if ( ! dm_pool_grow_object ( rh - > mem , " \0 " , 1 ) ) {
log_error ( " dm_report: Failed to terminate row " ) ;
goto bad ;
}
log_print ( " %s " , ( char * ) dm_pool_end_object ( rh - > mem ) ) ;
}
return 1 ;
bad :
dm_pool_abandon_object ( rh - > mem ) ;
return 0 ;
}
static int _output_as_columns ( struct dm_report * rh )
{
2008-11-04 01:14:30 +03:00
struct dm_list * fh , * rowh , * ftmp , * rtmp ;
2008-06-25 02:53:48 +04:00
struct row * row = NULL ;
struct dm_report_field * field ;
2007-01-16 21:04:15 +03:00
/* If headings not printed yet, calculate field widths and print them */
if ( ! ( rh - > flags & RH_HEADINGS_PRINTED ) )
_report_headings ( rh ) ;
/* Print and clear buffer */
2008-11-04 01:14:30 +03:00
dm_list_iterate_safe ( rowh , rtmp , & rh - > rows ) {
2007-01-16 21:04:15 +03:00
if ( ! dm_pool_begin_object ( rh - > mem , 512 ) ) {
2007-01-23 20:38:39 +03:00
log_error ( " dm_report: Unable to allocate output line " ) ;
2007-01-16 21:04:15 +03:00
return 0 ;
}
2008-11-04 01:14:30 +03:00
row = dm_list_item ( rowh , struct row ) ;
dm_list_iterate_safe ( fh , ftmp , & row - > fields ) {
field = dm_list_item ( fh , struct dm_report_field ) ;
2007-01-16 21:04:15 +03:00
if ( field - > props - > flags & FLD_HIDDEN )
continue ;
2008-06-25 02:53:48 +04:00
if ( ! _output_field ( rh , field ) )
goto bad ;
2007-01-16 21:04:15 +03:00
2008-11-04 01:14:30 +03:00
if ( ! dm_list_end ( & row - > fields , fh ) )
2008-04-19 19:50:18 +04:00
if ( ! dm_pool_grow_object ( rh - > mem , rh - > separator , 0 ) ) {
2007-01-23 20:38:39 +03:00
log_error ( " dm_report: Unable to extend output line " ) ;
goto bad ;
}
2008-06-25 02:53:48 +04:00
2008-11-04 01:14:30 +03:00
dm_list_del ( & field - > list ) ;
2007-01-16 21:04:15 +03:00
}
2007-01-23 20:38:39 +03:00
if ( ! dm_pool_grow_object ( rh - > mem , " \0 " , 1 ) ) {
log_error ( " dm_report: Unable to terminate output line " ) ;
goto bad ;
}
2007-01-16 21:04:15 +03:00
log_print ( " %s " , ( char * ) dm_pool_end_object ( rh - > mem ) ) ;
2008-11-04 01:14:30 +03:00
dm_list_del ( & row - > list ) ;
2007-01-16 21:04:15 +03:00
}
if ( row )
dm_pool_free ( rh - > mem , row ) ;
return 1 ;
2007-01-23 20:38:39 +03:00
bad :
2007-01-16 21:04:15 +03:00
dm_pool_abandon_object ( rh - > mem ) ;
return 0 ;
}
2008-06-25 02:53:48 +04:00
int dm_report_output ( struct dm_report * rh )
{
2008-11-04 01:14:30 +03:00
if ( dm_list_empty ( & rh - > rows ) )
2008-06-25 02:53:48 +04:00
return 1 ;
if ( ( rh - > flags & RH_SORT_REQUIRED ) )
_sort_rows ( rh ) ;
if ( ( rh - > flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS ) )
return _output_as_rows ( rh ) ;
else
return _output_as_columns ( rh ) ;
}