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