human.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. /* If INEXACT_STYLE is not human_round_to_even, and if easily
  65. possible, adjust VALUE according to the style. */
  66. static double
  67. adjust_value (enum human_inexact_style inexact_style, double value)
  68. {
  69. /* Do not use the floor or ceil functions, as that would mean
  70. linking with the standard math library, which is a porting pain.
  71. So leave the value alone if it is too large to easily round. */
  72. if (inexact_style != human_round_to_even && value < (uintmax_t) -1)
  73. {
  74. uintmax_t u = value;
  75. value = u + (inexact_style == human_ceiling && u != value);
  76. }
  77. return value;
  78. }
  79. /* Like human_readable_inexact, except always round to even. */
  80. char *
  81. human_readable (uintmax_t n, char *buf,
  82. int from_block_size, int output_block_size)
  83. {
  84. return human_readable_inexact (n, buf, from_block_size, output_block_size,
  85. human_round_to_even);
  86. }
  87. /* Convert N to a human readable format in BUF.
  88. N is expressed in units of FROM_BLOCK_SIZE. FROM_BLOCK_SIZE must
  89. be nonnegative.
  90. OUTPUT_BLOCK_SIZE must be nonzero. If it is positive, use units of
  91. OUTPUT_BLOCK_SIZE in the output number.
  92. Use INEXACT_STYLE to determine whether to take the ceiling or floor
  93. of any result that cannot be expressed exactly.
  94. If OUTPUT_BLOCK_SIZE is negative, use a format like "127k" if
  95. possible, using powers of -OUTPUT_BLOCK_SIZE; otherwise, use
  96. ordinary decimal format. Normally -OUTPUT_BLOCK_SIZE is either
  97. 1000 or 1024; it must be at least 2. Most people visually process
  98. strings of 3-4 digits effectively, but longer strings of digits are
  99. more prone to misinterpretation. Hence, converting to an
  100. abbreviated form usually improves readability. Use a suffix
  101. indicating which power is being used. For example, assuming
  102. -OUTPUT_BLOCK_SIZE is 1024, 8500 would be converted to 8.3k,
  103. 133456345 to 127M, 56990456345 to 53G, and so on. Numbers smaller
  104. than -OUTPUT_BLOCK_SIZE aren't modified. */
  105. char *
  106. human_readable_inexact (uintmax_t n, char *buf,
  107. int from_block_size, int output_block_size,
  108. enum human_inexact_style inexact_style)
  109. {
  110. uintmax_t amt;
  111. int base;
  112. int to_block_size;
  113. int tenths = 0;
  114. int power;
  115. char *p;
  116. /* 0 means adjusted N == AMT.TENTHS;
  117. 1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
  118. 2 means adjusted N == AMT.TENTHS + 0.05;
  119. 3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1. */
  120. int rounding = 0;
  121. if (output_block_size < 0)
  122. {
  123. base = -output_block_size;
  124. to_block_size = 1;
  125. }
  126. else
  127. {
  128. base = 0;
  129. to_block_size = output_block_size;
  130. }
  131. p = buf + LONGEST_HUMAN_READABLE;
  132. *p = '\0';
  133. #ifdef lint
  134. /* Suppress `used before initialized' warning. */
  135. power = 0;
  136. #endif
  137. /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE units. */
  138. {
  139. int multiplier;
  140. int divisor;
  141. int r2;
  142. int r10;
  143. if (to_block_size <= from_block_size
  144. ? (from_block_size % to_block_size != 0
  145. || (multiplier = from_block_size / to_block_size,
  146. (amt = n * multiplier) / multiplier != n))
  147. : (from_block_size == 0
  148. || to_block_size % from_block_size != 0
  149. || (divisor = to_block_size / from_block_size,
  150. r10 = (n % divisor) * 10,
  151. r2 = (r10 % divisor) * 2,
  152. amt = n / divisor,
  153. tenths = r10 / divisor,
  154. rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2),
  155. 0)))
  156. {
  157. /* Either the result cannot be computed easily using uintmax_t,
  158. or from_block_size is zero. Fall back on floating point.
  159. FIXME: This can yield answers that are slightly off. */
  160. double damt = n * (from_block_size / (double) to_block_size);
  161. if (! base)
  162. sprintf (buf, "%.0f", adjust_value (inexact_style, damt));
  163. else
  164. {
  165. double e = 1;
  166. power = 0;
  167. do
  168. {
  169. e *= base;
  170. power++;
  171. }
  172. while (e * base <= damt && power < sizeof suffixes - 1);
  173. damt /= e;
  174. sprintf (buf, "%.1f%c", adjust_value (inexact_style, damt),
  175. suffixes[power]);
  176. if (4 < strlen (buf))
  177. sprintf (buf, "%.0f%c",
  178. adjust_value (inexact_style, damt * 10) / 10,
  179. suffixes[power]);
  180. }
  181. return buf;
  182. }
  183. }
  184. /* Use power of BASE notation if adjusted AMT is large enough. */
  185. if (base && base <= amt)
  186. {
  187. power = 0;
  188. do
  189. {
  190. int r10 = (amt % base) * 10 + tenths;
  191. int r2 = (r10 % base) * 2 + (rounding >> 1);
  192. amt /= base;
  193. tenths = r10 / base;
  194. rounding = (r2 < base
  195. ? 0 < r2 + rounding
  196. : 2 + (base < r2 + rounding));
  197. power++;
  198. }
  199. while (base <= amt && power < sizeof suffixes - 1);
  200. *--p = suffixes[power];
  201. if (amt < 10)
  202. {
  203. if (2 * (1 - (int) inexact_style)
  204. < rounding + (tenths & (inexact_style == human_round_to_even)))
  205. {
  206. tenths++;
  207. rounding = 0;
  208. if (tenths == 10)
  209. {
  210. amt++;
  211. tenths = 0;
  212. }
  213. }
  214. if (amt < 10)
  215. {
  216. *--p = '0' + tenths;
  217. *--p = '.';
  218. tenths = rounding = 0;
  219. }
  220. }
  221. }
  222. if (inexact_style == human_ceiling
  223. ? 0 < tenths + rounding
  224. : inexact_style == human_round_to_even
  225. ? 5 < tenths + (2 < rounding + (amt & 1))
  226. : /* inexact_style == human_floor */ 0)
  227. {
  228. amt++;
  229. if (amt == base && power < sizeof suffixes - 1)
  230. {
  231. *p = suffixes[power + 1];
  232. *--p = '0';
  233. *--p = '.';
  234. amt = 1;
  235. }
  236. }
  237. do
  238. *--p = '0' + (int) (amt % 10);
  239. while ((amt /= 10) != 0);
  240. return p;
  241. }
  242. /* The default block size used for output. This number may change in
  243. the future as disks get larger. */
  244. #ifndef DEFAULT_BLOCK_SIZE
  245. # define DEFAULT_BLOCK_SIZE 1024
  246. #endif
  247. static char const *const block_size_args[] = { "human-readable", "si", 0 };
  248. static int const block_size_types[] = { -1024, -1000 };
  249. static int
  250. default_block_size (void)
  251. {
  252. return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
  253. }
  254. static strtol_error
  255. humblock (char const *spec, int *block_size)
  256. {
  257. int i;
  258. if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
  259. *block_size = default_block_size ();
  260. else if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_types)))
  261. *block_size = block_size_types[i];
  262. else
  263. {
  264. char *ptr;
  265. unsigned long val;
  266. strtol_error e = xstrtoul (spec, &ptr, 0, &val, "eEgGkKmMpPtTyYzZ0");
  267. if (e != LONGINT_OK)
  268. return e;
  269. if (*ptr)
  270. return LONGINT_INVALID_SUFFIX_CHAR;
  271. if ((int) val < 0 || val != (int) val)
  272. return LONGINT_OVERFLOW;
  273. *block_size = (int) val;
  274. }
  275. return LONGINT_OK;
  276. }
  277. void
  278. human_block_size (char const *spec, int report_errors, int *block_size)
  279. {
  280. strtol_error e = humblock (spec, block_size);
  281. if (*block_size == 0)
  282. {
  283. *block_size = default_block_size ();
  284. e = LONGINT_INVALID;
  285. }
  286. if (e != LONGINT_OK && report_errors)
  287. STRTOL_FATAL_ERROR (spec, _("block size"), e);
  288. }