logo

utils-extra

Collection of extra tools for Unixes git clone https://anongit.hacktivis.me/git/utils-extra.git/

humanize.c (2835B)


  1. // utils-extra: Collection of extra tools for Unixes
  2. // SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <errno.h> // EINVAL, ERANGE
  6. #include <limits.h> // LLONG_MIN, LLONG_MAX
  7. #include <stdbool.h> // bool
  8. #include <stdio.h> // fprintf, perror, sscanf
  9. #include <unistd.h> // opt*, getopt
  10. static void
  11. human_num(long double num, bool iec)
  12. {
  13. #define PFX 11
  14. char *si_prefixes[PFX] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"};
  15. char *iec_prefixes[PFX] = {
  16. "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"};
  17. unsigned div = iec ? 1024 : 1000;
  18. char **prefixes = iec ? iec_prefixes : si_prefixes;
  19. bool neg = false;
  20. if(num < 0)
  21. {
  22. neg = true;
  23. num = -num;
  24. }
  25. unsigned quotient = 0;
  26. while(num > div && quotient < (PFX - 1))
  27. {
  28. num /= div;
  29. quotient += 1;
  30. }
  31. if(neg) num = -num;
  32. // %g to avoid decimal separator when possible, %f to avoid %e exponent form
  33. printf((num > div ? "%Lf %s\n" : "%Lg %s\n"), num, prefixes[quotient]);
  34. }
  35. static void
  36. human_time(long int epoch)
  37. {
  38. int year = 0, month = 0, mday = 0, hour = 0, min = 0, sec = 0;
  39. year = epoch / 31556926; // round(year)
  40. epoch %= 31556926;
  41. month = epoch / 2629743; // year/12
  42. epoch %= 2629743;
  43. mday = epoch / 86400;
  44. epoch %= 86400;
  45. hour = epoch / 3600;
  46. epoch %= 3600;
  47. min = epoch / 60;
  48. epoch %= 60;
  49. sec = epoch;
  50. if(year > 0) printf("%dY ", year);
  51. if(month > 0) printf("%dM ", month);
  52. if(mday > 0) printf("%dD ", mday);
  53. if(hour > 0) printf("%dh ", hour);
  54. if(min > 0) printf("%dm ", min);
  55. if(sec > 0) printf("%ds", sec);
  56. printf("\n");
  57. }
  58. static void
  59. usage()
  60. {
  61. fprintf(stderr, "Usage: humanize [-bdt] number\n");
  62. }
  63. int
  64. main(int argc, char *argv[])
  65. {
  66. // default to -d
  67. bool iec = false, time = false;
  68. int c = -1;
  69. while((c = getopt(argc, argv, ":bdt")) != -1)
  70. {
  71. switch(c)
  72. {
  73. case 'b':
  74. iec = true;
  75. break;
  76. case 'd':
  77. iec = false;
  78. break;
  79. case 't':
  80. time = true;
  81. break;
  82. case ':':
  83. fprintf(stderr, "humanize: Error: Missing operand for option: '-%c'\n", optopt);
  84. usage();
  85. return 1;
  86. case '?':
  87. fprintf(stderr, "humanize: Error: Unrecognised option: '-%c'\n", optopt);
  88. usage();
  89. return 1;
  90. }
  91. }
  92. argc -= optind;
  93. argv += optind;
  94. if(argc < 1)
  95. {
  96. usage();
  97. return 1;
  98. }
  99. for(int argi = 0; argi < argc; argi++)
  100. {
  101. // Keep sscanf here for easier error handling, rest being pure math
  102. if(time)
  103. {
  104. long int epoch = 0;
  105. if(sscanf(argv[argi], "%ld", &epoch) < 1)
  106. {
  107. perror("humanize: sscanf");
  108. return 1;
  109. }
  110. human_time(epoch);
  111. }
  112. else
  113. {
  114. long double num = 0;
  115. if(sscanf(argv[argi], "%Lg", &num) < 1)
  116. {
  117. perror("humanize: sscanf");
  118. return 1;
  119. }
  120. human_num(num, iec);
  121. }
  122. }
  123. return 0;
  124. }