human

convert bytes to human readable formats
git clone git://git.2f30.org/human
Log | Files | Refs | README | LICENSE

human.c (3903B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <limits.h>
      4 #include <string.h>
      5 #include "arg.h"
      6 
      7 #define EXA  1152921504606846976
      8 #define PETA 1125899906842624
      9 #define TERA 1099511627776
     10 #define GIGA 1073741824
     11 #define MEGA 1048576
     12 #define KILO 1024
     13 
     14 #define DEFAULT_SCALE 0
     15 
     16 /*
     17  * Help, I need somebody
     18  * Help, not just anybody
     19  * Help, you know I need someone
     20  * Help!
     21  *
     22  */
     23 void usage (char *progname)
     24 {
     25     printf("usage: %s [-hbkmgt] <number>\n", progname);
     26 }
     27 
     28 /*
     29  * calculate a power of number
     30  * disclaimers: return no more than a "long" so use wisely...
     31  *
     32  */
     33 long power (long number, int pow)
     34 {
     35     return pow > 0 ? number * power(number, pow - 1) : 1;
     36 }
     37 
     38 /*
     39  * read the environment varaible "SCALE" and returns its value.
     40  * returns DEFAULT_SCALE if it does not return a number.
     41  *
     42  */
     43 int getscale()
     44 {
     45     /* would you rather use getenv() twice ? or allocate a pointer ? */
     46     char *scale = NULL;
     47     scale = getenv("SCALE");
     48 
     49     /* in atoi, we trust. maybe. */
     50     return scale ? atoi(scale) : DEFAULT_SCALE;
     51 }
     52 
     53 /*
     54  * calculate the best factorization for a number, depending on its value.
     55  * actual max factorisation is 1024^5 (EiB)
     56  *
     57  */
     58 char factorize (double number)
     59 {
     60     return number >= EXA  ? 'E' :
     61            number >= PETA ? 'P' :
     62            number >= TERA ? 'T' :
     63            number >= GIGA ? 'G' :
     64            number >= MEGA ? 'M' :
     65            number >= KILO ? 'K' :
     66            0;
     67 }
     68 
     69 /*
     70  * calculate a human-readable version of the given number, depending of the
     71  * factorisation level given.
     72  *
     73  */
     74 double humanize (double number, char factor)
     75 {
     76     int pow = 0;
     77 
     78     /* cascading switch. note the lack of "break" statements */
     79     switch (factor) {
     80         case 'E' : pow++;
     81         case 'P' : pow++;
     82         case 'T' : pow++;
     83         case 'G' : pow++;
     84         case 'M' : pow++;
     85         case 'K' : pow++; break;
     86         default  : return number;
     87     }
     88 
     89     /* return the number divided by the correct factorization level */
     90     return number /= power(1024, pow);
     91 }
     92 
     93 /*
     94  * finally print a number in human-readable format,
     95  *
     96  */
     97 int human(char* s, char fac)
     98 {
     99     int pow = 0;
    100     double number = 0;
    101 
    102     switch (s[strnlen(s, LINE_MAX) - 1]) {
    103         case 'E': pow++;
    104         case 'P': pow++;
    105         case 'T': pow++;
    106         case 'G': pow++;
    107         case 'M': pow++;
    108         case 'K': pow++;
    109         case 'B': s[strnlen(s, LINE_MAX) - 1] = 0;
    110     }
    111 
    112     /* get the number and convert it to bytes. If there is none, strtold will return 0 */
    113     number  = strtold(s, NULL);
    114     number *= power(1024, pow);
    115 
    116     if (number < 0) {
    117         fprintf(stderr, "I ain't gonna do that. Deal with it.");
    118         return -1;
    119     }
    120 
    121     /* use explicit factorization. otherwise, guess the best one */
    122     fac = fac > 0 ? fac : factorize(number);
    123 
    124     /* actually print the result, isn't that what we're here for after all ? */
    125     printf("%.*f", getscale(), humanize(number, fac));
    126     if (fac && fac != 'B') {
    127         putchar(fac);
    128     }
    129     putchar('\n');
    130     return 0;
    131 }
    132 
    133 int main (int argc, char **argv)
    134 {
    135     char fac = 0;
    136     char *argv0, in[LINE_MAX];
    137 
    138     /* only switches are use to force factorization */
    139     ARGBEGIN {
    140         case 'h': usage(argv0); exit(0); break;
    141         case 'e': fac = 'E'; break;
    142         case 'p': fac = 'P'; break;
    143         case 't': fac = 'T'; break;
    144         case 'g': fac = 'G'; break;
    145         case 'm': fac = 'M'; break;
    146         case 'k': fac = 'K'; break;
    147         case 'b': fac = 'B'; break;
    148         default: break;
    149     } ARGEND;
    150 
    151     if (argc > 0) {
    152         /* consume numbers from arguments, if any */
    153         while (argc --> 0) {
    154             human(*argv++, fac);
    155         }
    156     } else {
    157         /* read numbers from stdin if no args, one per line */
    158         while (fgets(in, LINE_MAX, stdin) != NULL) {
    159             /* overwrite the '\n' */
    160             in[strnlen(in, LINE_MAX) - 1] = 0;
    161             human(in, fac);
    162         }
    163     }
    164 
    165     return 0;
    166 }