4
0

human.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /* human.c -- print human readable file size
  2. Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 Free Software
  3. Foundation, Inc.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  15. /* Originally contributed by [email protected];
  16. --si, output block size selection, and large file support
  17. added by [email protected]. */
  18. #if HAVE_CONFIG_H
  19. # include <config.h>
  20. #endif
  21. #include <sys/types.h>
  22. #include <stdio.h>
  23. #if HAVE_LIMITS_H
  24. # include <limits.h>
  25. #endif
  26. #if HAVE_STRING_H
  27. # include <string.h>
  28. #else
  29. # include <strings.h>
  30. #endif
  31. #ifndef CHAR_BIT
  32. # define CHAR_BIT 8
  33. #endif
  34. #if HAVE_STDLIB_H
  35. # include <stdlib.h>
  36. #endif
  37. #ifndef HAVE_DECL_GETENV
  38. "this configure-time declaration test was not run"
  39. #endif
  40. #if !HAVE_DECL_GETENV
  41. char *getenv ();
  42. #endif
  43. #if ENABLE_NLS
  44. # include <libintl.h>
  45. # define _(Text) gettext (Text)
  46. #else
  47. # define _(Text) Text
  48. #endif
  49. #include <argmatch.h>
  50. #include <error.h>
  51. #include <xstrtol.h>
  52. #include "human.h"
  53. static const char suffixes[] =
  54. {
  55. 0, /* not used */
  56. 'K', /* kibi ('k' for kilo is a special case) */
  57. 'M', /* mega or mebi */
  58. 'G', /* giga or gibi */
  59. 'T', /* tera or tebi */
  60. 'P', /* peta or pebi */
  61. 'E', /* exa or exbi */
  62. 'Z', /* zetta or 2**70 */
  63. 'Y' /* yotta or 2**80 */
  64. };
  65. /* Generate into P[-1] (and possibly P[-2]) the proper suffix for
  66. POWER and BASE. Return the address of the generated suffix. */
  67. static char *
  68. generate_suffix_backwards (char *p, int power, int base)
  69. {
  70. char letter = suffixes[power];
  71. if (base == 1000)
  72. {
  73. *--p = 'B';
  74. if (power == 1)
  75. letter = 'k';
  76. }
  77. *--p = letter;
  78. return p;
  79. }
  80. /* If INEXACT_STYLE is not human_round_to_even, and if easily
  81. possible, adjust VALUE according to the style. */
  82. static double
  83. adjust_value (enum human_inexact_style inexact_style, double value)
  84. {
  85. /* Do not use the floor or ceil functions, as that would mean
  86. linking with the standard math library, which is a porting pain.
  87. So leave the value alone if it is too large to easily round. */
  88. if (inexact_style != human_round_to_even && value < (uintmax_t) -1)
  89. {
  90. uintmax_t u = value;
  91. value = u + (inexact_style == human_ceiling && u != value);
  92. }
  93. return value;
  94. }
  95. /* Like human_readable_inexact, except always round to even. */
  96. char *
  97. human_readable (uintmax_t n, char *buf,
  98. int from_block_size, int output_block_size)
  99. {
  100. return human_readable_inexact (n, buf, from_block_size, output_block_size,
  101. human_round_to_even);
  102. }
  103. /* Convert N to a human readable format in BUF.
  104. N is expressed in units of FROM_BLOCK_SIZE. FROM_BLOCK_SIZE must
  105. be nonnegative.
  106. OUTPUT_BLOCK_SIZE must be nonzero. If it is positive, use units of
  107. OUTPUT_BLOCK_SIZE in the output number.
  108. Use INEXACT_STYLE to determine whether to take the ceiling or floor
  109. of any result that cannot be expressed exactly.
  110. If OUTPUT_BLOCK_SIZE is negative, use a format like "127K" if
  111. possible, using powers of -OUTPUT_BLOCK_SIZE; otherwise, use
  112. ordinary decimal format. Normally -OUTPUT_BLOCK_SIZE is either
  113. 1000 or 1024; it must be at least 2. Most people visually process
  114. strings of 3-4 digits effectively, but longer strings of digits are
  115. more prone to misinterpretation. Hence, converting to an
  116. abbreviated form usually improves readability. Use a suffix
  117. indicating which power is being used. For example, assuming
  118. -OUTPUT_BLOCK_SIZE is 1024, 8500 would be converted to 8.3K,
  119. 133456345 to 127M, 56990456345 to 53G, and so on. Numbers smaller
  120. than -OUTPUT_BLOCK_SIZE aren't modified. If -OUTPUT_BLOCK_SIZE is
  121. 1024, append a "B" after any size letter. */
  122. char *
  123. human_readable_inexact (uintmax_t n, char *buf,
  124. int from_block_size, int output_block_size,
  125. enum human_inexact_style inexact_style)
  126. {
  127. uintmax_t amt;
  128. int base;
  129. int to_block_size;
  130. int tenths = 0;
  131. int power;
  132. char *p;
  133. /* 0 means adjusted N == AMT.TENTHS;
  134. 1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
  135. 2 means adjusted N == AMT.TENTHS + 0.05;
  136. 3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1. */
  137. int rounding = 0;
  138. if (output_block_size < 0)
  139. {
  140. base = -output_block_size;
  141. to_block_size = 1;
  142. }
  143. else
  144. {
  145. base = 0;
  146. to_block_size = output_block_size;
  147. }
  148. p = buf + LONGEST_HUMAN_READABLE;
  149. *p = '\0';
  150. #ifdef lint
  151. /* Suppress `used before initialized' warning. */
  152. power = 0;
  153. #endif
  154. /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE units. */
  155. {
  156. int multiplier;
  157. int divisor;
  158. int r2;
  159. int r10;
  160. if (to_block_size <= from_block_size
  161. ? (from_block_size % to_block_size != 0
  162. || (multiplier = from_block_size / to_block_size,
  163. (amt = n * multiplier) / multiplier != n))
  164. : (from_block_size == 0
  165. || to_block_size % from_block_size != 0
  166. || (divisor = to_block_size / from_block_size,
  167. r10 = (n % divisor) * 10,
  168. r2 = (r10 % divisor) * 2,
  169. amt = n / divisor,
  170. tenths = r10 / divisor,
  171. rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2),
  172. 0)))
  173. {
  174. /* Either the result cannot be computed easily using uintmax_t,
  175. or from_block_size is zero. Fall back on floating point.
  176. FIXME: This can yield answers that are slightly off. */
  177. double damt = n * (from_block_size / (double) to_block_size);
  178. if (! base)
  179. sprintf (buf, "%.0f", adjust_value (inexact_style, damt));
  180. else
  181. {
  182. char suffix[3];
  183. char const *psuffix;
  184. double e = 1;
  185. power = 0;
  186. do
  187. {
  188. e *= base;
  189. power++;
  190. }
  191. while (e * base <= damt && power < sizeof suffixes - 1);
  192. damt /= e;
  193. suffix[2] = '\0';
  194. psuffix = generate_suffix_backwards (suffix + 2, power, base);
  195. sprintf (buf, "%.1f%s",
  196. adjust_value (inexact_style, damt), psuffix);
  197. if (4 + (base == 1000) < strlen (buf))
  198. sprintf (buf, "%.0f%s",
  199. adjust_value (inexact_style, damt * 10) / 10, psuffix);
  200. }
  201. return buf;
  202. }
  203. }
  204. /* Use power of BASE notation if adjusted AMT is large enough. */
  205. if (base && base <= amt)
  206. {
  207. power = 0;
  208. do
  209. {
  210. int r10 = (amt % base) * 10 + tenths;
  211. int r2 = (r10 % base) * 2 + (rounding >> 1);
  212. amt /= base;
  213. tenths = r10 / base;
  214. rounding = (r2 < base
  215. ? 0 < r2 + rounding
  216. : 2 + (base < r2 + rounding));
  217. power++;
  218. }
  219. while (base <= amt && power < sizeof suffixes - 1);
  220. p = generate_suffix_backwards (p, power, base);
  221. if (amt < 10)
  222. {
  223. if (2 * (1 - (int) inexact_style)
  224. < rounding + (tenths & (inexact_style == human_round_to_even)))
  225. {
  226. tenths++;
  227. rounding = 0;
  228. if (tenths == 10)
  229. {
  230. amt++;
  231. tenths = 0;
  232. }
  233. }
  234. if (amt < 10)
  235. {
  236. *--p = '0' + tenths;
  237. *--p = '.';
  238. tenths = rounding = 0;
  239. }
  240. }
  241. }
  242. if (inexact_style == human_ceiling
  243. ? 0 < tenths + rounding
  244. : inexact_style == human_round_to_even
  245. ? 5 < tenths + (2 < rounding + (amt & 1))
  246. : /* inexact_style == human_floor */ 0)
  247. {
  248. amt++;
  249. if (amt == base && power < sizeof suffixes - 1)
  250. {
  251. *p = suffixes[power + 1];
  252. *--p = '0';
  253. *--p = '.';
  254. amt = 1;
  255. }
  256. }
  257. do
  258. *--p = '0' + (int) (amt % 10);
  259. while ((amt /= 10) != 0);
  260. return p;
  261. }
  262. /* The default block size used for output. This number may change in
  263. the future as disks get larger. */
  264. #ifndef DEFAULT_BLOCK_SIZE
  265. # define DEFAULT_BLOCK_SIZE 1024
  266. #endif
  267. static char const *const block_size_args[] = { "human-readable", "si", 0 };
  268. static int const block_size_types[] = { -1024, -1000 };
  269. static int
  270. default_block_size (void)
  271. {
  272. return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
  273. }
  274. static strtol_error
  275. humblock (char const *spec, int *block_size)
  276. {
  277. int i;
  278. if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
  279. *block_size = default_block_size ();
  280. else if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_types)))
  281. *block_size = block_size_types[i];
  282. else
  283. {
  284. char *ptr;
  285. unsigned long val;
  286. strtol_error e = xstrtoul (spec, &ptr, 0, &val, "eEgGkKmMpPtTyYzZ0");
  287. if (e != LONGINT_OK)
  288. return e;
  289. if (*ptr)
  290. return LONGINT_INVALID_SUFFIX_CHAR;
  291. if ((int) val < 0 || val != (int) val)
  292. return LONGINT_OVERFLOW;
  293. *block_size = (int) val;
  294. }
  295. return LONGINT_OK;
  296. }
  297. void
  298. human_block_size (char const *spec, int report_errors, int *block_size)
  299. {
  300. strtol_error e = humblock (spec, block_size);
  301. if (*block_size == 0)
  302. {
  303. *block_size = default_block_size ();
  304. e = LONGINT_INVALID;
  305. }
  306. if (e != LONGINT_OK && report_errors)
  307. STRTOL_FATAL_ERROR (spec, _("block size"), e);
  308. }