logo

9utils

Collection of utilities inspired by Plan9

freq.ha (2795B)


  1. // Collection of utilities inspired by Plan9
  2. // SPDX-FileCopyrightText: 2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. use getopt;
  5. use fmt;
  6. use os;
  7. use io;
  8. use ascii;
  9. use types;
  10. use strings;
  11. use encoding::utf8;
  12. fn is_unicode_cc(r: rune) bool = {
  13. let r = r: u32;
  14. if(r <= 0x0000 && r >= 0x001F) { return true; }; // C0 Controls
  15. if(r == 0x007F) { return true; }; // Delete
  16. if(r <= 0x0080 && r >= 0x009F) { return true; }; // C1 Controls
  17. return false;
  18. };
  19. export fn main() void = {
  20. let runic = false;
  21. const cmd = getopt::parse(os::args,
  22. "Print histogram frequency of bytes/characters",
  23. ('r', "Rune (unicode character) frequency instead of byte frequency"),
  24. "file ...",
  25. );
  26. defer getopt::finish(&cmd);
  27. for (let i = 0z; i < len(cmd.opts); i += 1) {
  28. const opt = cmd.opts[i];
  29. switch(opt.0) {
  30. case 'r' => runic = true;
  31. case => abort("unhandled option");
  32. };
  33. };
  34. if(runic) {
  35. let runes_count: [](rune, size) = [];
  36. let buf: [32]u8 = [0...];
  37. for(true) {
  38. let s = match(io::read(os::stdin, &buf)) {
  39. case let s: size =>
  40. yield s;
  41. case io::EOF =>
  42. break;
  43. case let e: io::error =>
  44. fmt::fatal("freq: I/O error: {}", io::strerror(e));
  45. };
  46. let buf_s = match(strings::fromutf8(buf[0..s])) {
  47. case let s: str =>
  48. yield s;
  49. case utf8::invalid =>
  50. fmt::fatal("freq: Invalid UTF-8");
  51. };
  52. let buf_iter = strings::iter(buf_s);
  53. for(true) {
  54. const r = match(strings::next(&buf_iter)) {
  55. case let r: rune =>
  56. yield r;
  57. case void =>
  58. break;
  59. };
  60. for(let i = 0z; true; i += 1) {
  61. if(i < len(runes_count)) {
  62. if(runes_count[i].0 == r) {
  63. runes_count[i].1 += 1;
  64. break;
  65. };
  66. } else {
  67. append(runes_count, (r, 1));
  68. break;
  69. };
  70. };
  71. };
  72. };
  73. for(let i = 0z; i < len(runes_count); i += 1) {
  74. const rune_c = runes_count[i];
  75. let c = if(!is_unicode_cc(rune_c.0)) {
  76. yield rune_c.0;
  77. } else {
  78. yield "-";
  79. };
  80. // 1114109 4177775 10fffd
  81. fmt::printfln("{0:7} {0:7o} {0:6x} {1} {2}", rune_c.0: u32, c, rune_c.1)!;
  82. };
  83. } else {
  84. // Stupid table of all bytes
  85. let bytes_count: [256]size = [0...];
  86. let buf: [32]u8 = [0...];
  87. for(true) {
  88. let s = match(io::read(os::stdin, &buf)) {
  89. case let s: size =>
  90. yield s;
  91. case io::EOF =>
  92. break;
  93. case let e: io::error =>
  94. fmt::fatal("freq: I/O error: {}", io::strerror(e));
  95. };
  96. for(let i = 0z; i < s; i += 1) {
  97. bytes_count[buf[i]] += 1;
  98. };
  99. };
  100. for(let i = 0u32; i < len(bytes_count); i += 1) {
  101. if(bytes_count[i] != 0) {
  102. let c = i: rune;
  103. let c = if(ascii::isprint(c)) {
  104. yield c;
  105. } else {
  106. yield "-";
  107. };
  108. fmt::printfln("{0:3} {0:03o} {0:02x} {1} {2}", i, c, bytes_count[i])!;
  109. };
  110. };
  111. };
  112. };