4
0

human.c 8.3 KB

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