Author: brane Date: Sun Nov 25 04:57:09 2018 New Revision: 1847384 URL: http://svn.apache.org/viewvc?rev=1847384&view=rev Log: Make the 'svn list -v' output more human-friendly.
This change introduces two new mini-features: - The -H, --human-readable option, which makes 'svn list -v' display file sizes in base-2 units; - Automatic adjustment of the width of the 'author' field in the verbose output, within a set of limits, so that long author names are truncated less often. In --human-readable form, the 'size' field is narrower, so the initial and maximum widths of the 'author' field are adjusted to maintain the same overall width of the 'svn list -v' output. * subversion/svn/cl.h (svn_cl__opt_state_t): Add flag 'human_readable'. * subversion/svn/svn.c (svn_cl__options): New option; -H, --human-readable. (svn_cl__cmd_table): Add the -H option to the 'list' command description. (sub_main): Handle the -H option. * subversion/svn/list-cmd.c (struct print_baton): Add fields 'human_readable', 'author_width' and 'max_author_width; and moved 'ctx' to the start of the struct. (initial_author_width, initial_human_readable_author_width, maximum_author_width, maximum_human_readable_author_width, normal_size_width, human_readable_size_width): New static constants. (get_human_readable_size): New. (print_dirent): Auto-adjust the width of the author field and select plain or human-friendly display for the size field. (svn_cl__list): Initialize the new fields in the print_baton structure. Modified: subversion/trunk/subversion/svn/cl.h subversion/trunk/subversion/svn/list-cmd.c subversion/trunk/subversion/svn/svn.c Modified: subversion/trunk/subversion/svn/cl.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1847384&r1=1847383&r2=1847384&view=diff ============================================================================== --- subversion/trunk/subversion/svn/cl.h (original) +++ subversion/trunk/subversion/svn/cl.h Sun Nov 25 04:57:09 2018 @@ -257,6 +257,7 @@ typedef struct svn_cl__opt_state_t svn_boolean_t adds_as_modification; /* update 'add vs add' no tree conflict */ svn_boolean_t vacuum_pristines; /* remove unreferenced pristines */ svn_boolean_t drop; /* drop shelf after successful unshelve */ + svn_boolean_t human_readable; /* show human-readable output */ enum svn_cl__viewspec_t { svn_cl__viewspec_unspecified = 0 /* default */, svn_cl__viewspec_classic, Modified: subversion/trunk/subversion/svn/list-cmd.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/list-cmd.c?rev=1847384&r1=1847383&r2=1847384&view=diff ============================================================================== --- subversion/trunk/subversion/svn/list-cmd.c (original) +++ subversion/trunk/subversion/svn/list-cmd.c Sun Nov 25 04:57:09 2018 @@ -40,8 +40,13 @@ /* Baton used when printing directory entries. */ struct print_baton { - svn_boolean_t verbose; svn_client_ctx_t *ctx; + svn_boolean_t verbose; + svn_boolean_t human_readable; + + /* Keep track of the width of the author field. */ + int author_width; + int max_author_width; /* To keep track of last seen external information. */ const char *last_external_parent_url; @@ -49,12 +54,73 @@ struct print_baton { svn_boolean_t in_external; }; +/* Starting and maximum width of the author field */ +static const int initial_author_width = 8; +static const int initial_human_readable_author_width = 14; +static const int maximum_author_width = 16; +static const int maximum_human_readable_author_width = 22; + +/* Width of the size field */ +static const int normal_size_width = 10; +static const int human_readable_size_width = 4; + /* Field flags required for this function */ static const apr_uint32_t print_dirent_fields = SVN_DIRENT_KIND; static const apr_uint32_t print_dirent_fields_verbose = ( SVN_DIRENT_KIND | SVN_DIRENT_SIZE | SVN_DIRENT_TIME | SVN_DIRENT_CREATED_REV | SVN_DIRENT_LAST_AUTHOR); +/* Converts a file size to human-readable form with base-2 unit suffix. + File sizes are never negative, so we don't handle that case. */ +static const char * +get_human_readable_size(svn_filesize_t size, apr_pool_t *scratch_pool) +{ + static const struct + { + svn_filesize_t mask; + char suffix; + } + order[] = + { + {APR_INT64_C(0x0000000000000000), 'B'}, /* byte */ + {APR_INT64_C(0x00000000000003FF), 'K'}, /* kibi */ + {APR_INT64_C(0x00000000000FFFFF), 'M'}, /* mibi */ + {APR_INT64_C(0x000000003FFFFFFF), 'G'}, /* gibi */ + {APR_INT64_C(0x000000FFFFFFFFFF), 'T'}, /* tibi */ + {APR_INT64_C(0x0003FFFFFFFFFFFF), 'E'}, /* exbi */ + {APR_INT64_C(0x0FFFFFFFFFFFFFFF), 'P'} /* pibi */ + }; + + const svn_filesize_t abs_size = ((size < 0) ? -size : size); + double human_readable_size; + + /* Find the size mask for the (absolute) file size. It would be sexy to + do a binary search here, but with only 7 elements in the array ... */ + apr_size_t index = sizeof(order) / sizeof(order[0]); + while (index > 0) + { + --index; + if (abs_size > order[index].mask) + break; + } + + /* Adjust the size to the given order of magnitude. + + This is division by (order[index].mask + 1), which is the base-2^10 + magnitude of the size; and that is the same as an arithmetic right + shift by (index * 10) bits. But we split it into an integer and a + floating-point division, so that we don't overflow the mantissa at + very large file sizes. */ + human_readable_size = (index == 0 ? (double)size + : (size >> 3 * index) / 128.0 / index); + + /* When the absolute adjusted size is < 10, show tenths of a unit, too. + NOTE: This will display the locale-specific decimal separator. */ + return apr_psprintf(scratch_pool, "%.*f%c", + (abs_size >> 10 * index < 10) ? 1 : 0, + human_readable_size, order[index].suffix); +} + /* This implements the svn_client_list_func2_t API, printing a single directory entry in text format. */ static svn_error_t * @@ -121,7 +187,10 @@ print_dirent(void *baton, apr_status_t apr_err; apr_size_t size; char timestr[20]; - const char *sizestr, *utf8_timestr; + const int sizewidth = (!pb->human_readable ? normal_size_width + : human_readable_size_width); + const char *sizestr = ""; + const char *utf8_timestr; /* svn_time_to_human_cstring gives us something *way* too long to use for this, so we have to roll our own. We include @@ -146,15 +215,36 @@ print_dirent(void *baton, /* we need it in UTF-8. */ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_timestr, timestr, scratch_pool)); - sizestr = apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT, - dirent->size); + /* We may have to adjust the width of th 'author' field. */ + if (dirent->last_author) + { + const int author_width = (int)strlen(dirent->last_author); + if (author_width > pb->author_width) + { + if (author_width < pb->max_author_width) + pb->author_width = author_width; + else + pb->author_width = pb->max_author_width; + } + } + + /* The format of the size field depends on the --human-readable flag. */ + if (dirent->kind == svn_node_file) + { + if (pb->human_readable) + sizestr = get_human_readable_size(dirent->size, scratch_pool); + else + sizestr = apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT, + dirent->size); + } return svn_cmdline_printf - (scratch_pool, "%7ld %-8.8s %c %10s %12s %s%s\n", + (scratch_pool, "%7ld %-*.*s %c %*s %12s %s%s\n", dirent->created_rev, + pb->author_width, pb->author_width, dirent->last_author ? dirent->last_author : " ? ", lock ? 'O' : ' ', - (dirent->kind == svn_node_file) ? sizestr : "", + sizewidth, sizestr, utf8_timestr, entryname, (dirent->kind == svn_node_dir) ? "/" : ""); @@ -332,6 +422,11 @@ svn_cl__list(apr_getopt_t *os, pb.ctx = ctx; pb.verbose = opt_state->verbose; + pb.human_readable = opt_state->human_readable; + pb.author_width = (!pb.human_readable ? initial_author_width + : initial_human_readable_author_width); + pb.max_author_width = (!pb.human_readable ? maximum_author_width + : maximum_human_readable_author_width); if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_immediates; Modified: subversion/trunk/subversion/svn/svn.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/svn.c?rev=1847384&r1=1847383&r2=1847384&view=diff ============================================================================== --- subversion/trunk/subversion/svn/svn.c (original) +++ subversion/trunk/subversion/svn/svn.c Sun Nov 25 04:57:09 2018 @@ -166,6 +166,7 @@ const apr_getopt_option_t svn_cl__option {"quiet", 'q', 0, N_("print nothing, or only summary information")}, {"recursive", 'R', 0, N_("descend recursively, same as --depth=infinity")}, {"non-recursive", 'N', 0, N_("obsolete")}, + {"human-readable",'H', 0, N_("show human-readable output")}, {"change", 'c', 1, N_("the change made by revision ARG (like -r ARG-1:ARG)\n" " " @@ -895,8 +896,15 @@ const svn_opt_subcommand_desc3_t svn_cl_ " Size (in bytes)\n" " Date and time of the last commit\n" )}, - {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml, - opt_include_externals, opt_search}, }, + {'r', 'v', 'R', 'H', opt_depth, opt_incremental, opt_xml, + opt_include_externals, opt_search}, + {{'H', N_("with --verbose, show file sizes with base-2\n" + " " + "unit suffixes (Byte, Kilobyte, Megabyte,\n" + " " + "Gigabyte, Terabyte and Petabyte), limiting\n" + " " + "the number of digits to three or less")}} }, { "lock", svn_cl__lock, {0}, {N_( "Lock working copy paths or URLs in the repository, so that\n" @@ -2477,6 +2485,9 @@ sub_main(int *exit_code, int argc, const case 'N': descend = FALSE; break; + case 'H': + opt_state.human_readable = TRUE; + break; case opt_depth: err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool); if (err)