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 }