GeneratePublicSuffixData.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * Copyright (c) 2023, Cameron Youell <cameronyouell@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/SourceGenerator.h>
  7. #include <AK/StringBuilder.h>
  8. #include <LibCore/ArgsParser.h>
  9. #include <LibCore/File.h>
  10. #include <LibMain/Main.h>
  11. ErrorOr<void> generate_header_file(Core::InputBufferedFile&, Core::File&);
  12. ErrorOr<void> generate_implementation_file(Core::InputBufferedFile&, Core::File&);
  13. static ErrorOr<NonnullOwnPtr<Core::InputBufferedFile>> open_file(StringView path, Core::File::OpenMode mode)
  14. {
  15. if (path.is_empty())
  16. return Error::from_string_literal("Provided path is empty, please provide all command line options");
  17. auto file = TRY(Core::File::open(path, mode));
  18. return Core::InputBufferedFile::create(move(file));
  19. }
  20. ErrorOr<int> serenity_main(Main::Arguments arguments)
  21. {
  22. StringView generated_header_path;
  23. StringView generated_implementation_path;
  24. StringView public_suffix_list_path;
  25. Core::ArgsParser args_parser;
  26. args_parser.add_option(generated_header_path, "Path to the header file to generate", "generated-header-path", 'h', "generated-header-path");
  27. args_parser.add_option(generated_implementation_path, "Path to the implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
  28. args_parser.add_option(public_suffix_list_path, "Path to the public suffix list", "public-suffix-list-path", 'p', "public-suffix-list-path");
  29. args_parser.parse(arguments);
  30. auto identifier_data = TRY(open_file(public_suffix_list_path, Core::File::OpenMode::Read));
  31. auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write));
  32. auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write));
  33. TRY(generate_header_file(*identifier_data, *generated_header_file));
  34. TRY(generate_implementation_file(*identifier_data, *generated_implementation_file));
  35. return 0;
  36. }
  37. ErrorOr<void> generate_header_file(Core::InputBufferedFile&, Core::File& file)
  38. {
  39. StringBuilder builder;
  40. SourceGenerator generator { builder };
  41. generator.append(R"~~~(
  42. #pragma once
  43. #include <AK/Forward.h>
  44. #include <AK/Trie.h>
  45. #include <AK/Variant.h>
  46. namespace URL {
  47. class PublicSuffixData {
  48. protected:
  49. PublicSuffixData();
  50. public:
  51. PublicSuffixData(PublicSuffixData const&) = delete;
  52. PublicSuffixData& operator=(PublicSuffixData const&) = delete;
  53. static PublicSuffixData* the()
  54. {
  55. static PublicSuffixData* s_the;
  56. if (!s_the)
  57. s_the = new PublicSuffixData;
  58. return s_the;
  59. }
  60. bool is_public_suffix(StringView host);
  61. ErrorOr<Optional<String>> get_public_suffix(StringView string);
  62. private:
  63. Trie<char, Empty> m_dictionary;
  64. };
  65. }
  66. )~~~");
  67. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  68. return {};
  69. }
  70. ErrorOr<void> generate_implementation_file(Core::InputBufferedFile& input, Core::File& file)
  71. {
  72. StringBuilder builder;
  73. SourceGenerator generator { builder };
  74. generator.append(R"~~~(
  75. #include <AK/String.h>
  76. #include <AK/Vector.h>
  77. #include <LibURL/PublicSuffixData.h>
  78. namespace URL {
  79. static constexpr auto s_public_suffixes = Array {)~~~");
  80. Array<u8, 1024> buffer {};
  81. while (TRY(input.can_read_line())) {
  82. auto line = TRY(input.read_line(buffer));
  83. if (line.starts_with("//"sv) || line.is_empty())
  84. continue;
  85. auto view = line.split_view("."sv);
  86. view.reverse();
  87. auto val = MUST(String::join("."sv, view));
  88. generator.set("line", val);
  89. generator.append(R"~~~(
  90. "@line@"sv,)~~~");
  91. }
  92. generator.append(R"~~~(
  93. };
  94. PublicSuffixData::PublicSuffixData()
  95. : m_dictionary('/')
  96. {
  97. // FIXME: Reduce the depth of this trie
  98. for (auto str : s_public_suffixes) {
  99. MUST(m_dictionary.insert(str.begin(), str.end(), Empty {}, [](auto const&, auto const&) -> Optional<Empty> { return {}; }));
  100. }
  101. }
  102. bool PublicSuffixData::is_public_suffix(StringView host)
  103. {
  104. auto it = host.begin();
  105. auto& node = m_dictionary.traverse_until_last_accessible_node(it, host.end());
  106. return it.is_end() && node.has_metadata();
  107. }
  108. ErrorOr<Optional<String>> PublicSuffixData::get_public_suffix(StringView string)
  109. {
  110. auto input = string.split_view("."sv);
  111. input.reverse();
  112. StringBuilder overall_search_string;
  113. StringBuilder search_string;
  114. for (auto part : input) {
  115. search_string.clear();
  116. TRY(search_string.try_append(TRY(overall_search_string.to_string())));
  117. TRY(search_string.try_append(part));
  118. if (is_public_suffix(search_string.string_view())) {
  119. overall_search_string.append(TRY(String::from_utf8(part)));
  120. overall_search_string.append("."sv);
  121. continue;
  122. }
  123. search_string.clear();
  124. TRY(search_string.try_append(TRY(overall_search_string.to_string())));
  125. TRY(search_string.try_append("*"sv));
  126. if (is_public_suffix(search_string.string_view())) {
  127. overall_search_string.append(TRY(String::from_utf8(part)));
  128. overall_search_string.append("."sv);
  129. continue;
  130. }
  131. break;
  132. }
  133. auto view = overall_search_string.string_view().split_view("."sv);
  134. view.reverse();
  135. StringBuilder return_string_builder;
  136. return_string_builder.join('.', view);
  137. auto returnString = TRY(return_string_builder.to_string());
  138. if (!returnString.is_empty())
  139. return returnString;
  140. return Optional<String> {};
  141. }
  142. }
  143. )~~~");
  144. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  145. return {};
  146. }