human.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 [email protected];
  15. --si, output block size selection, and large file support
  16. added by [email protected]. */
  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 power;
  100. char *p;
  101. /* 0 means adjusted N == AMT.TENTHS;
  102. 1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
  103. 2 means adjusted N == AMT.TENTHS + 0.05;
  104. 3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1. */
  105. int rounding = 0;
  106. if (output_block_size < 0)
  107. {
  108. base = -output_block_size;
  109. to_block_size = 1;
  110. }
  111. else
  112. {
  113. base = 0;
  114. to_block_size = output_block_size;
  115. }
  116. p = buf + LONGEST_HUMAN_READABLE;
  117. *p = '\0';
  118. #ifdef lint
  119. /* Suppress `used before initialized' warning. */
  120. power = 0;
  121. #endif
  122. /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE units. */
  123. {
  124. int multiplier;
  125. int divisor;
  126. int r2;
  127. int r10;
  128. if (to_block_size <= from_block_size
  129. ? (from_block_size % to_block_size != 0
  130. || (multiplier = from_block_size / to_block_size,
  131. (amt = n * multiplier) / multiplier != n))
  132. : (from_block_size == 0
  133. || to_block_size % from_block_size != 0
  134. || (divisor = to_block_size / from_block_size,
  135. r10 = (n % divisor) * 10,
  136. r2 = (r10 % divisor) * 2,
  137. amt = n / divisor,
  138. tenths = r10 / divisor,
  139. rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2),
  140. 0)))
  141. {
  142. /* Either the result cannot be computed easily using uintmax_t,
  143. or from_block_size is zero. Fall back on floating point.
  144. FIXME: This can yield answers that are slightly off. */
  145. double damt = n * (from_block_size / (double) to_block_size);
  146. if (! base)
  147. sprintf (buf, "%.0f", damt);
  148. else
  149. {
  150. double e = 1;
  151. power = 0;
  152. do
  153. {
  154. e *= base;
  155. power++;
  156. }
  157. while (e * base <= damt && power < sizeof suffixes - 1);
  158. damt /= e;
  159. sprintf (buf, "%.1f%c", damt, suffixes[power]);
  160. if (4 < strlen (buf))
  161. sprintf (buf, "%.0f%c", damt, suffixes[power]);
  162. }
  163. return buf;
  164. }
  165. }
  166. /* Use power of BASE notation if adjusted AMT is large enough. */
  167. if (base && base <= amt)
  168. {
  169. power = 0;
  170. do
  171. {
  172. int r10 = (amt % base) * 10 + tenths;
  173. int r2 = (r10 % base) * 2 + (rounding >> 1);
  174. amt /= base;
  175. tenths = r10 / base;
  176. rounding = (r2 < base
  177. ? 0 < r2 + rounding
  178. : 2 + (base < r2 + rounding));
  179. power++;
  180. }
  181. while (base <= amt && power < sizeof suffixes - 1);
  182. *--p = suffixes[power];
  183. if (amt < 10)
  184. {
  185. if (2 * (1 - (int) inexact_style)
  186. < rounding + (tenths & (inexact_style == human_round_to_even)))
  187. {
  188. tenths++;
  189. rounding = 0;
  190. if (tenths == 10)
  191. {
  192. amt++;
  193. tenths = 0;
  194. }
  195. }
  196. if (amt < 10)
  197. {
  198. *--p = '0' + tenths;
  199. *--p = '.';
  200. tenths = rounding = 0;
  201. }
  202. }
  203. }
  204. if (inexact_style == human_ceiling
  205. ? 0 < tenths + rounding
  206. : inexact_style == human_round_to_even
  207. ? 5 < tenths + (2 < rounding + (amt & 1))
  208. : /* inexact_style == human_floor */ 0)
  209. {
  210. amt++;
  211. if (amt == base && power < sizeof suffixes - 1)
  212. {
  213. *p = suffixes[power + 1];
  214. *--p = '0';
  215. *--p = '.';
  216. amt = 1;
  217. }
  218. }
  219. do
  220. *--p = '0' + (int) (amt % 10);
  221. while ((amt /= 10) != 0);
  222. return p;
  223. }
  224. /* The default block size used for output. This number may change in
  225. the future as disks get larger. */
  226. #ifndef DEFAULT_BLOCK_SIZE
  227. # define DEFAULT_BLOCK_SIZE 1024
  228. #endif
  229. static char const *const block_size_args[] = { "human-readable", "si", 0 };
  230. static int const block_size_types[] = { -1024, -1000 };
  231. static int
  232. default_block_size (void)
  233. {
  234. return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
  235. }
  236. static strtol_error
  237. humblock (char const *spec, int *block_size)
  238. {
  239. int i;
  240. if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
  241. *block_size = default_block_size ();
  242. else if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_types)))
  243. *block_size = block_size_types[i];
  244. else
  245. {
  246. char *ptr;
  247. unsigned long val;
  248. strtol_error e = xstrtoul (spec, &ptr, 0, &val, "eEgGkKmMpPtTyYzZ0");
  249. if (e != LONGINT_OK)
  250. return e;
  251. if (*ptr)
  252. return LONGINT_INVALID_SUFFIX_CHAR;
  253. if ((int) val < 0 || val != (int) val)
  254. return LONGINT_OVERFLOW;
  255. *block_size = (int) val;
  256. }
  257. return LONGINT_OK;
  258. }
  259. void
  260. human_block_size (char const *spec, int report_errors, int *block_size)
  261. {
  262. strtol_error e = humblock (spec, block_size);
  263. if (*block_size == 0)
  264. {
  265. *block_size = default_block_size ();
  266. e = LONGINT_INVALID;
  267. }
  268. if (e != LONGINT_OK && report_errors)
  269. STRTOL_FATAL_ERROR (spec, _("block size"), e);
  270. }