wc.cpp 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Emanuele Torre <torreemanuele6@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/String.h>
  8. #include <AK/Vector.h>
  9. #include <LibCore/ArgsParser.h>
  10. #include <ctype.h>
  11. #include <stdio.h>
  12. #include <sys/stat.h>
  13. #include <unistd.h>
  14. struct Count {
  15. String name;
  16. bool exists { true };
  17. unsigned lines { 0 };
  18. unsigned characters { 0 };
  19. unsigned words { 0 };
  20. size_t bytes { 0 };
  21. };
  22. bool g_output_line = false;
  23. bool g_output_byte = false;
  24. bool g_output_word = false;
  25. static void wc_out(const Count& count)
  26. {
  27. if (g_output_line)
  28. out("{:7} ", count.lines);
  29. if (g_output_word)
  30. out("{:7} ", count.words);
  31. if (g_output_byte)
  32. out("{:7} ", count.bytes);
  33. outln("{:>14}", count.name);
  34. }
  35. static Count get_count(const String& file_specifier)
  36. {
  37. Count count;
  38. FILE* file_pointer = nullptr;
  39. if (file_specifier == "-") {
  40. count.name = "";
  41. file_pointer = stdin;
  42. } else {
  43. count.name = file_specifier;
  44. if ((file_pointer = fopen(file_specifier.characters(), "r")) == nullptr) {
  45. warnln("wc: unable to open {}", file_specifier);
  46. count.exists = false;
  47. return count;
  48. }
  49. }
  50. bool start_a_new_word = true;
  51. int last_ch = EOF;
  52. for (int ch = fgetc(file_pointer); ch != EOF; ch = fgetc(file_pointer)) {
  53. last_ch = ch;
  54. count.bytes++;
  55. if (isspace(ch)) {
  56. start_a_new_word = true;
  57. if (ch == '\n')
  58. count.lines++;
  59. } else if (start_a_new_word) {
  60. start_a_new_word = false;
  61. count.words++;
  62. }
  63. }
  64. if (last_ch != '\n')
  65. count.lines++;
  66. if (file_pointer != stdin)
  67. fclose(file_pointer);
  68. return count;
  69. }
  70. static Count get_total_count(const Vector<Count>& counts)
  71. {
  72. Count total_count { "total" };
  73. for (auto& count : counts) {
  74. total_count.lines += count.lines;
  75. total_count.words += count.words;
  76. total_count.characters += count.characters;
  77. total_count.bytes += count.bytes;
  78. }
  79. return total_count;
  80. }
  81. int main(int argc, char** argv)
  82. {
  83. if (pledge("stdio rpath", nullptr) < 0) {
  84. perror("pledge");
  85. return 1;
  86. }
  87. Vector<const char*> file_specifiers;
  88. Core::ArgsParser args_parser;
  89. args_parser.add_option(g_output_line, "Output line count", "lines", 'l');
  90. args_parser.add_option(g_output_byte, "Output byte count", "bytes", 'c');
  91. args_parser.add_option(g_output_word, "Output word count", "words", 'w');
  92. args_parser.add_positional_argument(file_specifiers, "File to process", "file", Core::ArgsParser::Required::No);
  93. args_parser.parse(argc, argv);
  94. if (!g_output_line && !g_output_byte && !g_output_word)
  95. g_output_line = g_output_byte = g_output_word = true;
  96. Vector<Count> counts;
  97. for (const auto& file_specifier : file_specifiers)
  98. counts.append(get_count(file_specifier));
  99. if (pledge("stdio", nullptr) < 0) {
  100. perror("pledge");
  101. return 1;
  102. }
  103. if (file_specifiers.is_empty())
  104. counts.append(get_count("-"));
  105. else if (file_specifiers.size() > 1)
  106. counts.append(get_total_count(counts));
  107. for (const auto& count : counts) {
  108. if (count.exists)
  109. wc_out(count);
  110. }
  111. return 0;
  112. }