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 }