btrfs-progs: add options to tune units for fi df output
authorDavid Sterba <dsterba@suse.cz>
Sat, 30 Aug 2014 18:27:00 +0000 (15:27 -0300)
committerDavid Sterba <dsterba@suse.cz>
Fri, 10 Oct 2014 16:58:28 +0000 (18:58 +0200)
The size unit format is a longstanding annoyance. This patch is based on
the work of Nils and Alexandre and enhances the options. It's possible
to select raw bytes, SI-based or IEC-based compact units (human
frientdly) or a fixed base from kilobytes to terabytes. The default is
compact human readable IEC-based, no change to current version.

CC: Nils Steinger <nst@voidptr.de>
CC: Alexandre Oliva <oliva@gnu.org>
Reviewed-by: Hugo Mills <hugo@carfax.org.uk>
Signed-off-by: David Sterba <dsterba@suse.cz>
Documentation/btrfs-filesystem.txt
cmds-filesystem.c
utils.c
utils.h

index b0d5820..6e63d2c 100644 (file)
@@ -17,8 +17,31 @@ resizing, defragment.
 
 SUBCOMMAND
 ----------
-*df* <path> [<path>...]::
+*df* [options] <path>::
 Show space usage information for a mount point.
++
+`Options`
++
+-b|--raw::::
+raw numbers in bytes, without the 'B' suffix
+-h::::
+print human friendly numbers, base 1024, this is the default
+-H::::
+print human friendly numbers, base 1000
+--iec::::
+select the 1024 base for the following options, according to the IEC standard
+--si::::
+select the 1000 base for the following options, according to the SI standard
+-k|--kbytes::::
+show sizes in KiB, or kB with --si
+-m|--mbytes::::
+show sizes in MiB, or mB with --si
+-g|--gbytes::::
+show sizes in GiB, or gB with --si
+-t|--tbytes::::
+show sizes in TiB, or tB with --si
+
+If conflicting options are passed, the last one takes precedence.
 
 *show* [--mounted|--all-devices|<path>|<uuid>|<device>|<label>]::
 Show the btrfs filesystem with some additional info.
index f543914..bb5881e 100644 (file)
@@ -113,9 +113,18 @@ static const char * const filesystem_cmd_group_usage[] = {
 };
 
 static const char * const cmd_df_usage[] = {
-       "btrfs filesystem df <path>",
-       "Show space usage information for a mount point",
-       NULL
+       "btrfs filesystem df [options] <path>",
+       "Show space usage information for a mount point",
+       "-b|--raw           raw numbers in bytes",
+       "-h                 human friendly numbers, base 1024 (default)",
+       "-H                 human friendly numbers, base 1000",
+       "--iec              use 1024 as a base (KiB, MiB, GiB, TiB)",
+       "--si               use 1000 as a base (kB, mB, gB, tB)",
+       "-k|--kbytes        show sizes in KiB, or kB with --si",
+       "-m|--mbytes        show sizes in MiB, or mB with --si",
+       "-g|--gbytes        show sizes in GiB, or gB with --si",
+       "-t|--tbytes        show sizes in TiB, or tB with --si",
+       NULL
 };
 
 static char *group_type_str(u64 flag)
@@ -209,7 +218,7 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
        return 0;
 }
 
-static void print_df(struct btrfs_ioctl_space_args *sargs)
+static void print_df(struct btrfs_ioctl_space_args *sargs, unsigned unit_mode)
 {
        u64 i;
        struct btrfs_ioctl_space_info *sp = sargs->spaces;
@@ -218,8 +227,8 @@ static void print_df(struct btrfs_ioctl_space_args *sargs)
                printf("%s, %s: total=%s, used=%s\n",
                        group_type_str(sp->flags),
                        group_profile_str(sp->flags),
-                       pretty_size(sp->total_bytes),
-                       pretty_size(sp->used_bytes));
+                       pretty_size_mode(sp->total_bytes, unit_mode),
+                       pretty_size_mode(sp->used_bytes, unit_mode));
        }
 }
 
@@ -229,12 +238,62 @@ static int cmd_df(int argc, char **argv)
        int ret;
        int fd;
        char *path;
-       DIR  *dirstream = NULL;
+       DIR *dirstream = NULL;
+       unsigned unit_mode = UNITS_DEFAULT;
 
-       if (check_argc_exact(argc, 2))
+       optind = 1;
+       while (1) {
+               int long_index;
+               static const struct option long_options[] = {
+                       { "raw", no_argument, NULL, 'b'},
+                       { "kbytes", no_argument, NULL, 'k'},
+                       { "mbytes", no_argument, NULL, 'm'},
+                       { "gbytes", no_argument, NULL, 'g'},
+                       { "tbytes", no_argument, NULL, 't'},
+                       { "si", no_argument, NULL, 256},
+                       { "iec", no_argument, NULL, 257},
+               };
+               int c = getopt_long(argc, argv, "bhHkmgt", long_options,
+                                       &long_index);
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'b':
+                       unit_mode = UNITS_RAW;
+                       break;
+               case 'k':
+                       units_set_base(&unit_mode, UNITS_KBYTES);
+                       break;
+               case 'm':
+                       units_set_base(&unit_mode, UNITS_MBYTES);
+                       break;
+               case 'g':
+                       units_set_base(&unit_mode, UNITS_GBYTES);
+                       break;
+               case 't':
+                       units_set_base(&unit_mode, UNITS_TBYTES);
+                       break;
+               case 'h':
+                       unit_mode = UNITS_HUMAN_BINARY;
+                       break;
+               case 'H':
+                       unit_mode = UNITS_HUMAN_DECIMAL;
+                       break;
+               case 256:
+                       units_set_mode(&unit_mode, UNITS_DECIMAL);
+                       break;
+               case 257:
+                       units_set_mode(&unit_mode, UNITS_BINARY);
+                       break;
+               default:
+                       usage(cmd_df_usage);
+               }
+       }
+
+       if (check_argc_max(argc, optind + 1))
                usage(cmd_df_usage);
 
-       path = argv[1];
+       path = argv[optind];
 
        fd = open_file_or_dir(path, &dirstream);
        if (fd < 0) {
@@ -244,7 +303,7 @@ static int cmd_df(int argc, char **argv)
        ret = get_df(fd, &sargs);
 
        if (ret == 0) {
-               print_df(sargs);
+               print_df(sargs, unit_mode);
                free(sargs);
        } else {
                fprintf(stderr, "ERROR: get_df failed %s\n", strerror(-ret));
diff --git a/utils.c b/utils.c
index bdb7399..e86292a 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -1297,35 +1297,38 @@ out:
 static const char* unit_suffix_binary[] =
        { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
 static const char* unit_suffix_decimal[] =
-       { "B", "KB", "MB", "GB", "TB", "PB", "EB"};
+       { "B", "kB", "mB", "gB", "tB", "pB", "eB"};
 
-int pretty_size_snprintf(u64 size, char *str, size_t str_size, int unit_mode)
+int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mode)
 {
        int num_divs;
        float fraction;
-       int base = 0;
+       u64 base = 0;
+       int mult = 0;
        const char** suffix = NULL;
        u64 last_size;
 
        if (str_size == 0)
                return 0;
 
-       if (unit_mode == UNITS_RAW) {
+       if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) {
                snprintf(str, str_size, "%llu", size);
                return 0;
        }
 
-       if (unit_mode == UNITS_BINARY) {
+       if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_BINARY) {
                base = 1024;
+               mult = 1024;
                suffix = unit_suffix_binary;
-       } else if (unit_mode == UNITS_DECIMAL) {
+       } else if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_DECIMAL) {
                base = 1000;
+               mult = 1000;
                suffix = unit_suffix_decimal;
        }
 
        /* Unknown mode */
        if (!base) {
-               fprintf(stderr, "INTERNAL ERROR: unknown unit base, mode %d",
+               fprintf(stderr, "INTERNAL ERROR: unknown unit base, mode %d\n",
                                unit_mode);
                assert(0);
                return -1;
@@ -1333,11 +1336,22 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, int unit_mode)
 
        num_divs = 0;
        last_size = size;
-
-       while (size >= base) {
-               last_size = size;
-               size /= base;
-               num_divs++;
+       switch (unit_mode & UNITS_MODE_MASK) {
+       case UNITS_TBYTES: base *= mult; num_divs++;
+       case UNITS_GBYTES: base *= mult; num_divs++;
+       case UNITS_MBYTES: base *= mult; num_divs++;
+       case UNITS_KBYTES: num_divs++;
+                          break;
+       case UNITS_BYTES:
+                          base = 1;
+                          num_divs = 0;
+                          break;
+       default:
+               while (size >= mult) {
+                       last_size = size;
+                       size /= mult;
+                       num_divs++;
+               }
        }
 
        if (num_divs >= ARRAY_SIZE(unit_suffix_binary)) {
@@ -2377,3 +2391,17 @@ int test_isdir(const char *path)
 
        return S_ISDIR(st.st_mode);
 }
+
+void units_set_mode(unsigned *units, unsigned mode)
+{
+       unsigned base = *units & UNITS_MODE_MASK;
+
+       *units = base | mode;
+}
+
+void units_set_base(unsigned *units, unsigned base)
+{
+       unsigned mode = *units & ~UNITS_MODE_MASK;
+
+       *units = base | mode;
+}
diff --git a/utils.h b/utils.h
index e2b1ba8..aed03f2 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -48,12 +48,26 @@ void fixup_argv0(char **argv, const char *token);
 void set_argv0(char **argv);
 
 /*
- * Output mode of byte units
+ * Output modes of size
  */
-#define UNITS_RAW                      (1)
-#define UNITS_BINARY                   (2)
-#define UNITS_DECIMAL                  (3)
-#define UNITS_HUMAN                    UNITS_BINARY
+#define UNITS_RESERVED                 (0)
+#define UNITS_BYTES                    (1)
+#define UNITS_KBYTES                   (2)
+#define UNITS_MBYTES                   (3)
+#define UNITS_GBYTES                   (4)
+#define UNITS_TBYTES                   (5)
+#define UNITS_RAW                      (1U << UNITS_MODE_SHIFT)
+#define UNITS_BINARY                   (2U << UNITS_MODE_SHIFT)
+#define UNITS_DECIMAL                  (3U << UNITS_MODE_SHIFT)
+#define UNITS_MODE_MASK                        ((1U << UNITS_MODE_SHIFT) - 1)
+#define UNITS_MODE_SHIFT               (8)
+#define UNITS_HUMAN_BINARY             (UNITS_BINARY)
+#define UNITS_HUMAN_DECIMAL            (UNITS_DECIMAL)
+#define UNITS_HUMAN                    (UNITS_HUMAN_BINARY)
+#define UNITS_DEFAULT                  (UNITS_HUMAN)
+
+void units_set_mode(unsigned *units, unsigned mode);
+void units_set_base(unsigned *units, unsigned base);
 
 int make_btrfs(int fd, const char *device, const char *label,
               char *fs_uuid, u64 blocks[6], u64 num_bytes, u32 nodesize,
@@ -76,12 +90,12 @@ int check_mounted_where(int fd, const char *file, char *where, int size,
 int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
                                 int super_offset);
 
-int pretty_size_snprintf(u64 size, char *str, size_t str_bytes, int unit_mode);
-#define pretty_size(size)      pretty_size_mode(size, UNITS_BINARY)
+int pretty_size_snprintf(u64 size, char *str, size_t str_bytes, unsigned unit_mode);
+#define pretty_size(size)      pretty_size_mode(size, UNITS_DEFAULT)
 #define pretty_size_mode(size, mode)                                         \
        ({                                                                    \
                static __thread char _str[32];                                \
-               (void)pretty_size_snprintf((size), _str, sizeof(_str), mode); \
+               (void)pretty_size_snprintf((size), _str, sizeof(_str), (mode)); \
                _str;                                                         \
        })