From 27ab0a7b65e79fc64a65f947f115ba6f8c370bac Mon Sep 17 00:00:00 2001
From: Michal Sojka <sojkam1@fel.cvut.cz>
Date: Sun, 2 Feb 2014 22:44:57 +0100
Subject: [PATCH] Display human readable sizes in panels
When midnight commander displays the size of a file, it tries to
display as much digits as fits into the size column (7 characters
wide). The result is that for a 3 MB file it shows, for example,
3010050. In many cases, it is not important to know the exact number
of bytes in a file, but only an approximate size (3 MB). Short numbers
are more "human friendly".
This patch adds a configuration option that enables displaying such
human readable sizes in panels. The "human readable" means that at
most three digits are displayed for each file size. This is
accomplished by modifying function size_trunc_len(). Since the comment
of this function says that floating point should be avoided by any
means, the implementation is not as trivial as it could be. It
displays floating point numbers by displaying integer and fractional
parts separately as integers.
The effect of this patch is shown in the following table. "si" and
"hr" denote the values use_si and human_readable parameters of the
size_trunc_len() function. The table shows the results of the function
for different sizes.
CURRENT THIS PATCH
size | !si!hr si!hr !si hr si hr
-----------|--------------------------------
950 | 950 950 950 950
1001 | 1001 1001 0.97K 1.00k
1005 | 1005 1005 0.98K 1.01k
1023 | 1023 1023 0.99K 1.02k
1024 | 1024 1024 0.99K 1.02k
9849 | 9849 9849 9.61K 9.85k
12050 | 12050 12050 11.8K 12.1k
99940 | 99940 99940 97.5K 99.9k
100000 | 100000 100000 97.6K 100k
102399 | 102399 102399 100K 102k
102400 | 102400 102400 100K 102k
210050 | 210050 210050 205K 210k
3010050 | 3010050 3010050 2.87M 3.01m
43010050 | 42002K 43010k 41.0M 43.0m
1072693248 | 1023M 1073m 0.99G 1.07g
Currently, the decimal separator (".") is hardcoded and independent of
user's locale.
If anyone wants to test the patch, the table was created with the code
below.
void print(uintmax_t size)
{
char buffer[50];
int units = 0;
gboolean use_si = TRUE;
gboolean human_readable = TRUE;
int len = 7;
printf("%10ld", size);
size_trunc_len (buffer, len, size, units, !use_si, !human_readable);
printf("%8s", buffer);
size_trunc_len (buffer, len, size, units, use_si, !human_readable);
printf("%8s", buffer);
size_trunc_len (buffer, len, size, units, !use_si, human_readable);
printf("%8s", buffer);
size_trunc_len (buffer, len, size, units, use_si, human_readable);
printf("%8s", buffer);
printf("\n");
}
int main(int argc, char *argv[])
{
print(950);
print(1001);
// ...
}
---
lib/util.c | 27 +++++++++++++++++++++++++--
lib/util.h | 2 +-
src/filemanager/boxes.c | 1 +
src/filemanager/chown.c | 2 +-
src/filemanager/filegui.c | 8 ++++----
src/filemanager/info.c | 6 +++---
src/filemanager/panel.c | 6 +++---
src/setup.c | 2 ++
src/setup.h | 1 +
src/viewer/display.c | 2 +-
10 files changed, 42 insertions(+), 15 deletions(-)
diff --git a/lib/util.c b/lib/util.c
index a34d1b8..093d947 100644
a
|
b
|
size_trunc_sep (uintmax_t size, gboolean use_si) |
413 | 413 | */ |
414 | 414 | |
415 | 415 | void |
416 | | size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si) |
| 416 | size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si, |
| 417 | gboolean human_readable) |
417 | 418 | { |
418 | 419 | /* Avoid taking power for every file. */ |
419 | 420 | /* *INDENT-OFF* */ |
… |
… |
size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gbool |
454 | 455 | static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL }; |
455 | 456 | |
456 | 457 | const char *const *sfx = use_si ? suffix_lc : suffix; |
| 458 | unsigned base = use_si ? 1000 : 1024; |
| 459 | unsigned human_digits; |
457 | 460 | int j = 0; |
458 | 461 | |
459 | 462 | if (len == 0) |
… |
… |
size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gbool |
484 | 487 | size += size_remain; /* Re-add remainder lost by division/multiplication */ |
485 | 488 | } |
486 | 489 | |
| 490 | if (human_readable && len < 5) |
| 491 | human_readable = FALSE; /* Human readable form needs 5 characters */ |
| 492 | |
487 | 493 | for (j = units; sfx[j] != NULL; j++) |
488 | 494 | { |
489 | 495 | if (size == 0) |
… |
… |
size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gbool |
501 | 507 | break; |
502 | 508 | } |
503 | 509 | |
504 | | if (size < power10[len - (j > 0 ? 1 : 0)]) |
| 510 | #define DIV_ROUND(a, b) (((a) + ((b)/2))/(b)) |
| 511 | if ((!human_readable && size < power10[len - (j > 0 ? 1 : 0)]) || |
| 512 | (human_readable && size < 1000)) |
505 | 513 | { |
506 | 514 | g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, sfx[j]); |
507 | 515 | break; |
508 | 516 | } |
| 517 | else if (human_readable && DIV_ROUND (size, 10) < base && sfx[j + 1]) |
| 518 | { |
| 519 | human_digits = DIV_ROUND (size, 10) * 1000 / base; /* 0 - 999 */ |
| 520 | g_snprintf (buffer, len + 1, "%u.%02u%s", |
| 521 | human_digits / 100, human_digits % 100, sfx[j + 1]); |
| 522 | break; |
| 523 | } |
| 524 | else if (human_readable && DIV_ROUND (size, 100) < base && sfx[j + 1]) |
| 525 | { |
| 526 | human_digits = DIV_ROUND (size, 100) * 1000 / base; /* 0 - 999 */ |
| 527 | g_snprintf (buffer, len + 1, "%u.%01u%s", |
| 528 | human_digits / 10, human_digits % 10, sfx[j + 1]); |
| 529 | break; |
| 530 | } |
| 531 | #undef DIV_ROUND |
509 | 532 | |
510 | 533 | /* Powers of 1000 or 1024, with rounding. */ |
511 | 534 | if (use_si) |
diff --git a/lib/util.h b/lib/util.h
index 24f55c1..49e6530 100644
a
|
b
|
const char *size_trunc_sep (uintmax_t size, gboolean use_si); |
106 | 106 | * not including trailing 0. BUFFER should be at least LEN+1 long. |
107 | 107 | * |
108 | 108 | * Units: size units (0=bytes, 1=Kbytes, 2=Mbytes, etc.) */ |
109 | | void size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si); |
| 109 | void size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si, gboolean human_readable); |
110 | 110 | const char *string_perm (mode_t mode_bits); |
111 | 111 | |
112 | 112 | const char *extension (const char *); |
diff --git a/src/filemanager/boxes.c b/src/filemanager/boxes.c
index bd15ae9..7ae3fde 100644
a
|
b
|
panel_options_box (void) |
536 | 536 | QUICK_START_GROUPBOX (N_("Main options")), |
537 | 537 | QUICK_CHECKBOX (N_("Show mi&ni-status"), &panels_options.show_mini_info, NULL), |
538 | 538 | QUICK_CHECKBOX (N_("Use SI si&ze units"), &panels_options.kilobyte_si, NULL), |
| 539 | QUICK_CHECKBOX (N_("Human readable &size"), &panels_options.human_readable, NULL), |
539 | 540 | QUICK_CHECKBOX (N_("Mi&x all files"), &panels_options.mix_all_files, NULL), |
540 | 541 | QUICK_CHECKBOX (N_("Show &backup files"), &panels_options.show_backups, NULL), |
541 | 542 | QUICK_CHECKBOX (N_("Show &hidden files"), &panels_options.show_dot_files, NULL), |
diff --git a/src/filemanager/chown.c b/src/filemanager/chown.c
index 5040b16..bd56c61 100644
a
|
b
|
chown_cmd (void) |
367 | 367 | chown_label (0, str_trunc (fname, GW - 4)); |
368 | 368 | chown_label (1, str_trunc (get_owner (sf_stat.st_uid), GW - 4)); |
369 | 369 | chown_label (2, str_trunc (get_group (sf_stat.st_gid), GW - 4)); |
370 | | size_trunc_len (buffer, GW - 4, sf_stat.st_size, 0, panels_options.kilobyte_si); |
| 370 | size_trunc_len (buffer, GW - 4, sf_stat.st_size, 0, panels_options.kilobyte_si, panels_options.human_readable); |
371 | 371 | chown_label (3, buffer); |
372 | 372 | chown_label (4, string_perm (sf_stat.st_mode)); |
373 | 373 | |
diff --git a/src/filemanager/filegui.c b/src/filemanager/filegui.c
index b75fa24..82ed527 100644
a
|
b
|
overwrite_query_dialog (file_op_context_t * ctx, enum OperationMode mode) |
557 | 557 | |
558 | 558 | /* source date and size */ |
559 | 559 | size_trunc_len (fsize_buffer, sizeof (fsize_buffer), ui->s_stat->st_size, 0, |
560 | | panels_options.kilobyte_si); |
| 560 | panels_options.kilobyte_si, panels_options.human_readable); |
561 | 561 | ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), fsize_buffer, y++); |
562 | 562 | rd_xlen = max (rd_xlen, label2->cols + 8); |
563 | 563 | /* destination date and size */ |
564 | 564 | size_trunc_len (fsize_buffer, sizeof (fsize_buffer), ui->d_stat->st_size, 0, |
565 | | panels_options.kilobyte_si); |
| 565 | panels_options.kilobyte_si, panels_options.human_readable); |
566 | 566 | ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), fsize_buffer, y++); |
567 | 567 | rd_xlen = max (rd_xlen, label2->cols + 8); |
568 | 568 | |
… |
… |
file_progress_show_total (FileOpTotalContext * tctx, file_op_context_t * ctx, |
1009 | 1009 | |
1010 | 1010 | if (ui->total_bytes_label != NULL) |
1011 | 1011 | { |
1012 | | size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si); |
| 1012 | size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si, panels_options.human_readable); |
1013 | 1013 | if (!ctx->progress_totals_computed) |
1014 | 1014 | g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2); |
1015 | 1015 | else |
1016 | 1016 | { |
1017 | | size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si); |
| 1017 | size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si, panels_options.human_readable); |
1018 | 1018 | g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3); |
1019 | 1019 | } |
1020 | 1020 | |
diff --git a/src/filemanager/info.c b/src/filemanager/info.c
index 6397723..335a984 100644
a
|
b
|
info_show_info (WInfo * info) |
167 | 167 | { |
168 | 168 | char buffer1[6], buffer2[6]; |
169 | 169 | |
170 | | size_trunc_len (buffer1, 5, myfs_stats.avail, 1, panels_options.kilobyte_si); |
171 | | size_trunc_len (buffer2, 5, myfs_stats.total, 1, panels_options.kilobyte_si); |
| 170 | size_trunc_len (buffer1, 5, myfs_stats.avail, 1, panels_options.kilobyte_si, panels_options.human_readable); |
| 171 | size_trunc_len (buffer2, 5, myfs_stats.total, 1, panels_options.kilobyte_si, panels_options.human_readable); |
172 | 172 | tty_printf (_("Free space: %s/%s (%d%%)"), buffer1, buffer2, |
173 | 173 | myfs_stats.total == 0 ? 0 : |
174 | 174 | (int) (100 * (long double) myfs_stats.avail / myfs_stats.total)); |
… |
… |
info_show_info (WInfo * info) |
221 | 221 | #endif |
222 | 222 | { |
223 | 223 | char buffer[10]; |
224 | | size_trunc_len (buffer, 9, st.st_size, 0, panels_options.kilobyte_si); |
| 224 | size_trunc_len (buffer, 9, st.st_size, 0, panels_options.kilobyte_si, panels_options.human_readable); |
225 | 225 | tty_printf (_("Size: %s"), buffer); |
226 | 226 | #ifdef HAVE_STRUCT_STAT_ST_BLOCKS |
227 | 227 | tty_printf (ngettext (" (%ld block)", " (%ld blocks)", |
diff --git a/src/filemanager/panel.c b/src/filemanager/panel.c
index 45e4759..b21f14e 100644
a
|
b
|
string_file_size (file_entry_t * fe, int len) |
510 | 510 | else |
511 | 511 | #endif |
512 | 512 | { |
513 | | size_trunc_len (buffer, (unsigned int) len, fe->st.st_size, 0, panels_options.kilobyte_si); |
| 513 | size_trunc_len (buffer, (unsigned int) len, fe->st.st_size, 0, panels_options.kilobyte_si, panels_options.human_readable); |
514 | 514 | } |
515 | 515 | return buffer; |
516 | 516 | } |
… |
… |
show_free_space (WPanel * panel) |
1141 | 1141 | char buffer1[6], buffer2[6], tmp[BUF_SMALL]; |
1142 | 1142 | |
1143 | 1143 | size_trunc_len (buffer1, sizeof (buffer1) - 1, myfs_stats.avail, 1, |
1144 | | panels_options.kilobyte_si); |
| 1144 | panels_options.kilobyte_si, panels_options.human_readable); |
1145 | 1145 | size_trunc_len (buffer2, sizeof (buffer2) - 1, myfs_stats.total, 1, |
1146 | | panels_options.kilobyte_si); |
| 1146 | panels_options.kilobyte_si, panels_options.human_readable); |
1147 | 1147 | g_snprintf (tmp, sizeof (tmp), " %s/%s (%d%%) ", buffer1, buffer2, |
1148 | 1148 | myfs_stats.total == 0 ? 0 : |
1149 | 1149 | (int) (100 * (long double) myfs_stats.avail / myfs_stats.total)); |
diff --git a/src/setup.c b/src/setup.c
index 20c7145..8c46278 100644
a
|
b
|
int saving_setup; |
130 | 130 | panels_options_t panels_options = { |
131 | 131 | .show_mini_info = TRUE, |
132 | 132 | .kilobyte_si = FALSE, |
| 133 | .human_readable = FALSE, |
133 | 134 | .mix_all_files = FALSE, |
134 | 135 | .show_backups = TRUE, |
135 | 136 | .show_dot_files = TRUE, |
… |
… |
static const struct |
389 | 390 | } panels_ini_options[] = { |
390 | 391 | { "show_mini_info", &panels_options.show_mini_info }, |
391 | 392 | { "kilobyte_si", &panels_options.kilobyte_si }, |
| 393 | { "human_readable", &panels_options.human_readable }, |
392 | 394 | { "mix_all_files", &panels_options.mix_all_files }, |
393 | 395 | { "show_backups", &panels_options.show_backups }, |
394 | 396 | { "show_dot_files", &panels_options.show_dot_files }, |
diff --git a/src/setup.h b/src/setup.h
index 4d43117..e7e9378 100644
a
|
b
|
typedef struct |
37 | 37 | gboolean show_mini_info; /* If true, show the mini-info on the panel */ |
38 | 38 | gboolean kilobyte_si; /* If TRUE, SI units (1000 based) will be used for larger units |
39 | 39 | * (kilobyte, megabyte, ...). If FALSE, binary units (1024 based) will be used */ |
| 40 | gboolean human_readable; /* If TRUE, at most three digits are used to display the size of the files. */ |
40 | 41 | gboolean mix_all_files; /* If FALSE then directories are shown separately from files */ |
41 | 42 | gboolean show_backups; /* If TRUE, show files ending in ~ */ |
42 | 43 | gboolean show_dot_files; /* If TRUE, show files starting with a dot */ |
diff --git a/src/viewer/display.c b/src/viewer/display.c
index ed3ed2a..9849a33 100644
a
|
b
|
mcview_display_status (mcview_t * view) |
156 | 156 | char buffer[BUF_TRUNC_LEN + 1]; |
157 | 157 | |
158 | 158 | size_trunc_len (buffer, BUF_TRUNC_LEN, mcview_get_filesize (view), 0, |
159 | | panels_options.kilobyte_si); |
| 159 | panels_options.kilobyte_si, panels_options.human_readable); |
160 | 160 | tty_printf ("%9" PRIuMAX "/%s%s %s", (uintmax_t) view->dpy_end, |
161 | 161 | buffer, mcview_may_still_grow (view) ? "+" : " ", |
162 | 162 | #ifdef HAVE_CHARSET |