human_readable.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. * June 30, 2001 Manuel Novoa III
  3. *
  4. * All-integer version (hey, not everyone has floating point) of
  5. * make_human_readable_str, modified from similar code I had written
  6. * for busybox several months ago.
  7. *
  8. * Notes:
  9. * 1) I'm using an unsigned long long to hold the product size * block_size,
  10. * as df (which calls this routine) could request a representation of a
  11. * partition size in bytes > max of unsigned long. If long longs aren't
  12. * available, it would be possible to do what's needed using polynomial
  13. * representations (say, powers of 1024) and manipulating coefficients.
  14. * The base ten "bytes" output could be handled similarly.
  15. *
  16. * 2) This routine always outputs a decimal point and a tenths digit when
  17. * display_unit != 0. Hence, it isn't uncommon for the returned string
  18. * to have a length of 5 or 6.
  19. *
  20. * It might be nice to add a flag to indicate no decimal digits in
  21. * that case. This could be either an additional parameter, or a
  22. * special value of display_unit. Such a flag would also be nice for du.
  23. *
  24. * Some code to omit the decimal point and tenths digit is sketched out
  25. * and "#if 0"'d below.
  26. */
  27. #include <stdio.h>
  28. #include "libbb.h"
  29. const char *make_human_readable_str(unsigned long long size,
  30. unsigned long block_size, unsigned long display_unit)
  31. {
  32. /* The code will adjust for additional (appended) units. */
  33. static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' };
  34. static const char fmt[] = "%Lu";
  35. static const char fmt_tenths[] = "%Lu.%d%c";
  36. static char str[21]; /* Sufficient for 64 bit unsigned integers. */
  37. unsigned long long val;
  38. int frac;
  39. const char *u;
  40. const char *f;
  41. u = zero_and_units;
  42. f = fmt;
  43. frac = 0;
  44. val = size * block_size;
  45. if (val == 0) {
  46. return u;
  47. }
  48. if (display_unit) {
  49. val += display_unit/2; /* Deal with rounding. */
  50. val /= display_unit; /* Don't combine with the line above!!! */
  51. } else {
  52. ++u;
  53. while ((val >= KILOBYTE)
  54. && (u < zero_and_units + sizeof(zero_and_units) - 1)) {
  55. f = fmt_tenths;
  56. ++u;
  57. frac = ((((int)(val % KILOBYTE)) * 10) + (KILOBYTE/2)) / KILOBYTE;
  58. val /= KILOBYTE;
  59. }
  60. if (frac >= 10) { /* We need to round up here. */
  61. ++val;
  62. frac = 0;
  63. }
  64. #if 0
  65. /* Sample code to omit decimal point and tenths digit. */
  66. if ( /* no_tenths */ 1 ) {
  67. if ( frac >= 5 ) {
  68. ++val;
  69. }
  70. f = "%Lu%*c" /* fmt_no_tenths */ ;
  71. frac = 1;
  72. }
  73. #endif
  74. }
  75. /* If f==fmt then 'frac' and 'u' are ignored. */
  76. snprintf(str, sizeof(str), f, val, frac, *u);
  77. return str;
  78. }