human.c 7.7 KB

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