LCOV - code coverage report
Current view: top level - libtld - tld_compiler.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 879 1297 67.8 %
Date: 2022-02-19 13:28:04 Functions: 83 95 87.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* TLD library -- TLD, domain name, and sub-domain extraction
       2             :  * Copyright (c) 2011-2022  Made to Order Software Corp.  All Rights Reserved
       3             :  *
       4             :  * Permission is hereby granted, free of charge, to any person obtaining a
       5             :  * copy of this software and associated documentation files (the
       6             :  * "Software"), to deal in the Software without restriction, including
       7             :  * without limitation the rights to use, copy, modify, merge, publish,
       8             :  * distribute, sublicense, and/or sell copies of the Software, and to
       9             :  * permit persons to whom the Software is furnished to do so, subject to
      10             :  * the following conditions:
      11             :  *
      12             :  * The above copyright notice and this permission notice shall be included
      13             :  * in all copies or substantial portions of the Software.
      14             :  *
      15             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      16             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      17             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      18             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      19             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      20             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      21             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      22             :  */
      23             : 
      24             : /** \file
      25             :  * \brief Implementation of the TLD parser library.
      26             :  *
      27             :  * This file includes all the functions available in the C library
      28             :  * of libtld that pertain to the parsing of URIs and extraction of
      29             :  * TLDs.
      30             :  */
      31             : 
      32             : // self
      33             : //
      34             : #include    "libtld/tld_compiler.h"
      35             : #include    "libtld/tld_file.h"
      36             : 
      37             : 
      38             : // C++ lib
      39             : //
      40             : #include    <algorithm>
      41             : #include    <fstream>
      42             : #include    <iostream>
      43             : #include    <sstream>
      44             : #include    <iomanip>
      45             : 
      46             : 
      47             : // C lib
      48             : //
      49             : #include    <dirent.h>
      50             : #include    <string.h>
      51             : #include    <sys/stat.h>
      52             : 
      53             : 
      54             : 
      55             : 
      56             : 
      57        7346 : tld_string::tld_string(string_id_t id, std::string const & s)
      58             :     : f_id(id)
      59        7346 :     , f_string(s)
      60             : {
      61        7346 : }
      62             : 
      63             : 
      64       54282 : string_id_t tld_string::get_id() const
      65             : {
      66       54282 :     return f_id;
      67             : }
      68             : 
      69             : 
      70    96133884 : std::string const & tld_string::get_string() const
      71             : {
      72    96133884 :     return f_string;
      73             : }
      74             : 
      75             : 
      76    79690674 : std::string::size_type tld_string::length() const
      77             : {
      78    79690674 :     return f_string.length();
      79             : }
      80             : 
      81             : 
      82        1720 : void tld_string::set_found_in(string_id_t id)
      83             : {
      84        1720 :     f_found_in = id;
      85        1720 : }                   
      86             : 
      87             : 
      88    88687408 : string_id_t tld_string::get_found_in() const
      89             : {
      90    88687408 :     return f_found_in;
      91             : }
      92             : 
      93             : 
      94             : 
      95             : 
      96             : 
      97             : 
      98             : 
      99             : 
     100             : 
     101             : 
     102       61628 : string_id_t tld_string_manager::add_string(std::string const & s)
     103             : {
     104       61628 :     string_id_t id(find_string(s));
     105             : 
     106       61628 :     if(id == STRING_ID_NULL)
     107             :     {
     108        7346 :         id = ++f_next_id;
     109       14692 :         tld_string::pointer_t str(std::make_shared<tld_string>(id, s));
     110        7346 :         f_strings_by_string[s] = str;
     111        7346 :         f_strings_by_id[id] = str;
     112             : 
     113        7346 :         f_total_length += s.length();
     114        7346 :         if(s.length() > f_max_length)
     115             :         {
     116           7 :             f_max_length = s.length();
     117             :         }
     118             :     }
     119             : 
     120       61628 :     return id;
     121             : }
     122             : 
     123             : 
     124       61628 : string_id_t tld_string_manager::find_string(std::string const & s)
     125             : {
     126       61628 :     auto it(f_strings_by_string.find(s));
     127       61628 :     if(it == f_strings_by_string.end())
     128             :     {
     129        7346 :         return STRING_ID_NULL;
     130             :     }
     131             : 
     132       54282 :     return it->second->get_id();
     133             : }
     134             : 
     135             : 
     136      357945 : std::string tld_string_manager::get_string(string_id_t id) const
     137             : {
     138      357945 :     auto it(f_strings_by_id.find(id));
     139      357945 :     if(it == f_strings_by_id.end())
     140             :     {
     141           0 :         return std::string();
     142             :     }
     143      357945 :     return it->second->get_string();
     144             : }
     145             : 
     146             : 
     147           0 : string_id_t tld_string_manager::get_next_string_id() const
     148             : {
     149           0 :     return f_next_id;
     150             : }
     151             : 
     152             : 
     153           2 : std::size_t tld_string_manager::size() const
     154             : {
     155           2 :     return f_strings_by_id.size();
     156             : }
     157             : 
     158             : 
     159           1 : std::size_t tld_string_manager::max_length() const
     160             : {
     161           1 :     return f_max_length;
     162             : }
     163             : 
     164             : 
     165           1 : std::size_t tld_string_manager::total_length() const
     166             : {
     167           1 :     return f_total_length;
     168             : }
     169             : 
     170             : 
     171           1 : std::string const & tld_string_manager::compressed_strings() const
     172             : {
     173           1 :     return f_merged_strings;
     174             : }
     175             : 
     176             : 
     177           2 : std::size_t tld_string_manager::compressed_length() const
     178             : {
     179           2 :     return f_merged_strings.length();
     180             : }
     181             : 
     182             : 
     183    32238730 : std::string::size_type tld_string_manager::end_start_match(std::string const & s1, std::string const & s2)
     184             : {
     185    32238730 :     char const *c1(s1.c_str() + s1.length());
     186    32238730 :     char const *c2(s2.c_str());
     187   209038933 :     for(std::string::size_type max(std::min(s1.length(), s2.length()) - 1);
     188   209038933 :         max > 0;
     189             :         --max)
     190             :     {
     191   178169055 :         if(strncmp(c1 - max, c2, max) == 0)
     192             :         {
     193     1368852 :             return max;
     194             :         }
     195             :     }
     196    30869878 :     return 0;
     197             : }
     198             : 
     199             : 
     200           1 : void tld_string_manager::merge_strings()
     201             : {
     202             :     // we want to save all the strings as P-strings (a.k.a. "Pascal" strings)
     203             :     // with the size of the string inside our table; as a result, this means
     204             :     // all our strings can be merged in one superstring (i.e. no '\0' at all)
     205             :     //
     206             :     // (i.e. the implementation of the tld library makes use of a length in
     207             :     // various places, so having the length pre-computed allows us to avoid
     208             :     // an strlen() call each time we need it)
     209             : 
     210             :     // first we check for strings fully included in another string; those
     211             :     // do not need any special handling so we eliminate them first
     212             :     //
     213             : //std::cout << "info: find included strings" << std::endl;
     214        7312 :     for(auto & s1 : f_strings_by_id)
     215             :     {
     216    46790097 :         for(auto & s2 : f_strings_by_id)
     217             :         {
     218    93568872 :             if(s1.first != s2.first
     219    46777846 :             && s2.second->get_found_in() == STRING_ID_NULL
     220    39844512 :             && s1.second->length() > s2.second->length()
     221    62427172 :             && s1.second->get_string().find(s2.second->get_string()) != std::string::npos)
     222             :             {
     223        1650 :                 s2.second->set_found_in(s1.first);
     224        1650 :                 ++f_included_count;
     225        1650 :                 f_included_length += s2.second->length();
     226        1650 :                 break;
     227             :             }
     228             :         }
     229             :     }
     230             : 
     231             :     // at this time I implemented a simplified superstring implementation;
     232             :     // I just look for the longest merge between two strings and use that
     233             :     // then move on to the next string; it's probably 50% correct already
     234             :     //
     235             :     // note: at the time I tested this one, I saved just under 2Kb so I
     236             :     // don't want to sweat it too much either, that said, with all the
     237             :     // compression, we save 2/3rd of the space (at the moment, a little
     238             :     // under 50Kb final instead of over 150Kb without any compression)
     239             :     //
     240             : //std::cout << "info: find mergeable strings" << std::endl;
     241          36 :     while(merge_two_strings()); // TODO: This is dead slow...
     242             : 
     243             :     // now we have all the strings merged (or not if not possible)
     244             :     // create one big resulting string of the result
     245             :     //
     246             : //std::cout << "info: generate final super-string" << std::endl;
     247        7347 :     for(auto s : f_strings_by_id)
     248             :     {
     249        7346 :         if(s.second->get_found_in() == STRING_ID_NULL)
     250             :         {
     251        5626 :             f_merged_strings += s.second->get_string();
     252             :         }
     253             :     }
     254             : //std::cout << "final super-string: ["
     255             : //    << f_merged_strings
     256             : //    << "] length="
     257             : //    << f_merged_strings.length()
     258             : //    << std::endl;
     259           1 : }
     260             : 
     261             : 
     262          36 : bool tld_string_manager::merge_two_strings()
     263             : {
     264          36 :     string_id_t id1(STRING_ID_NULL);
     265          36 :     string_id_t id2(STRING_ID_NULL);
     266          36 :     std::string::size_type best(0);
     267      263862 :     for(auto & s1 : f_strings_by_id)
     268             :     {
     269      588312 :         if(s1.second->get_found_in() == STRING_ID_NULL
     270      730818 :         && f_strings_reviewed.find(s1.first) == f_strings_reviewed.end())
     271             :         {
     272    41649782 :             for(auto & s2 : f_strings_by_id)
     273             :             {
     274    83288172 :                 if(s1.first != s2.first
     275    41644086 :                 && s2.second->get_found_in() == STRING_ID_NULL)
     276             :                 {
     277    32238730 :                     std::string const & str1(s1.second->get_string());
     278    32238730 :                     std::string const & str2(s2.second->get_string());
     279    32238730 :                     std::string::size_type const d(end_start_match(str1, str2));
     280             : 
     281    32238730 :                     if(d > best)
     282             :                     {
     283         100 :                         best = d;
     284         100 :                         id1 = s1.first;
     285         100 :                         id2 = s2.first;
     286             :                     }
     287             :                 }
     288             :             }
     289        5696 :             f_strings_reviewed.insert(s1.first);
     290             :         }
     291             :     }
     292             : 
     293          36 :     if(best > 0)
     294             :     {
     295          35 :         std::string const & str1(f_strings_by_id[id1]->get_string());
     296          35 :         std::string const & str2(f_strings_by_id[id2]->get_string());
     297             : 
     298          70 :         std::string const merged(str1 + str2.substr(best));
     299             : #if 0
     300             : std::cout << "\n"
     301             : << "Found " << best
     302             : << ": [" << str1
     303             : << "] vs [" << str2
     304             : << "] -> [" << merged
     305             : << "]" << std::endl;
     306             : #endif
     307             : 
     308          35 :         string_id_t merged_id(add_string(merged));
     309             : 
     310          35 :         f_strings_by_id[id1]->set_found_in(merged_id);
     311          35 :         f_strings_by_id[id2]->set_found_in(merged_id);
     312             : 
     313          35 :         ++f_merged_count;
     314          35 :         f_merged_length += best;
     315          35 :         return true;
     316             :     }
     317             : 
     318             :     // no merge happened
     319             :     //
     320           1 :     return false;
     321             : }
     322             : 
     323             : 
     324           1 : std::size_t tld_string_manager::included_count() const
     325             : {
     326           1 :     return f_included_count;
     327             : }
     328             : 
     329             : 
     330           1 : std::size_t tld_string_manager::included_length() const
     331             : {
     332           1 :     return f_included_length;
     333             : }
     334             : 
     335             : 
     336           1 : std::size_t tld_string_manager::merged_count() const
     337             : {
     338           1 :     return f_merged_count;
     339             : }
     340             : 
     341             : 
     342           1 : std::size_t tld_string_manager::merged_length() const
     343             : {
     344           1 :     return f_merged_length;
     345             : }
     346             : 
     347             : 
     348        7311 : std::size_t tld_string_manager::get_string_offset(std::string const & s) const
     349             : {
     350        7311 :     return f_merged_strings.find(s);
     351             : }
     352             : 
     353             : 
     354        7311 : std::size_t tld_string_manager::get_string_offset(string_id_t id) const
     355             : {
     356        7311 :     auto it(f_strings_by_id.find(id));
     357        7311 :     if(it == f_strings_by_id.end())
     358             :     {
     359           0 :         return std::string::npos;
     360             :     }
     361             : 
     362        7311 :     return get_string_offset(it->second->get_string());
     363             : }
     364             : 
     365             : 
     366             : 
     367             : 
     368             : 
     369             : 
     370             : 
     371             : 
     372       10464 : void tld_tag_manager::add(tags_t const & tags)
     373             : {
     374             :     // transform the tags in an array as we will save in the output
     375             :     //
     376       10910 :     tags_table_t const table(tags_to_table(tags));
     377             : 
     378             :     // if another description has the exact same tags, do not duplicate
     379             :     //
     380     2345509 :     for(auto const & it : f_tags)
     381             :     {
     382     2345063 :         if(it == table)
     383             :         {
     384       10018 :             return;
     385             :         }
     386             :     }
     387             : 
     388             :     // save the result in the vector if not found
     389             :     //
     390         446 :     f_tags.push_back(table);
     391             : }
     392             : 
     393             : 
     394           1 : void tld_tag_manager::merge()
     395             : {
     396           2 :     std::set<int> processed_tags;
     397           2 :     std::set<int> processed_intermediates;
     398           2 :     std::set<int> unhandled_tags;
     399           2 :     std::set<int> unhandled_intermediates;
     400           2 :     tags_vector_t intermediate_tags;
     401             : 
     402         447 :     for(auto t1(f_tags.begin()); t1 != f_tags.end(); ++t1)
     403             :     {
     404         446 :         processed_tags.insert(std::distance(f_tags.begin(), t1));
     405             : 
     406         446 :         auto best_match(f_tags.end());
     407         446 :         auto best_swapped_match(f_tags.end());
     408         446 :         auto best_intermediate_match(intermediate_tags.end());
     409         446 :         auto best_swapped_intermediate_match(intermediate_tags.end());
     410         446 :         std::size_t best(0);
     411         446 :         std::size_t best_swapped(0);
     412             : 
     413             :         // check against other unmerged tags
     414             :         //
     415      199362 :         for(auto t2(f_tags.begin()); t2 != f_tags.end(); ++t2)
     416             :         {
     417      198916 :             if(processed_tags.find(std::distance(f_tags.begin(), t2)) != processed_tags.end())
     418             :             {
     419             :                 // this was already used up, ignore
     420       99862 :                 continue;
     421             :             }
     422             : 
     423       99054 :             std::size_t const d1(end_start_match(*t1, *t2));
     424       99054 :             if(d1 > best)
     425             :             {
     426           0 :                 best = d1;
     427           0 :                 best_match = t2;
     428             :             }
     429             : 
     430       99054 :             std::size_t const d2(end_start_match(*t2, *t1));
     431       99054 :             if(d2 > best_swapped)
     432             :             {
     433           1 :                 best_swapped = d2;
     434           1 :                 best_swapped_match = t2;
     435             :             }
     436             :         }
     437             : 
     438             :         // check against already merged tags
     439             :         //
     440         882 :         for(auto ti(intermediate_tags.begin()); ti != intermediate_tags.end(); ++ti)
     441             :         {
     442         436 :             if(processed_intermediates.find(std::distance(intermediate_tags.begin(), ti)) != processed_intermediates.end())
     443             :             {
     444             :                 // TBD: I may just want to remove those used up intermediates
     445             :                 //      and I think I don't need this test at all
     446           0 :                 continue;
     447             :             }
     448             : 
     449         436 :             std::size_t const d1(end_start_match(*t1, *ti));
     450         436 :             if(d1 > best)
     451             :             {
     452           0 :                 best = d1;
     453           0 :                 best_intermediate_match = ti;
     454             :             }
     455             : 
     456         436 :             std::size_t const d2(end_start_match(*ti, *t1));
     457         436 :             if(d2 > best_swapped)
     458             :             {
     459           0 :                 best_swapped = d2;
     460           0 :                 best_swapped_intermediate_match = ti;
     461             :             }
     462             :         }
     463             : 
     464        1338 :         if(best_intermediate_match != intermediate_tags.end()
     465        1338 :         || best_swapped_intermediate_match != intermediate_tags.end())
     466             :         {
     467           0 :             if(best_swapped > best)
     468             :             {
     469           0 :                 tags_table_t merged(*best_swapped_intermediate_match);
     470           0 :                 merged.insert(
     471           0 :                           merged.end()
     472           0 :                         , t1->begin() + best_swapped
     473           0 :                         , t1->end());
     474           0 :                 intermediate_tags.push_back(merged);
     475             : 
     476           0 :                 processed_intermediates.insert(std::distance(intermediate_tags.begin(), best_swapped_intermediate_match));
     477             :             }
     478           0 :             else if(best > best_swapped)
     479             :             {
     480           0 :                 tags_table_t merged(*t1);
     481           0 :                 merged.insert(
     482           0 :                           merged.end()
     483           0 :                         , best_intermediate_match->begin() + best
     484           0 :                         , best_intermediate_match->end());
     485           0 :                 intermediate_tags.push_back(merged);
     486             : 
     487           0 :                 processed_intermediates.insert(std::distance(intermediate_tags.begin(), best_intermediate_match));
     488             :             }
     489             :         }
     490        1338 :         else if(best_match != f_tags.end()
     491        1338 :              || best_swapped_match != f_tags.end())
     492             :         {
     493             :             // we found a best match meaning that we can merged t1 & t2 a bit
     494             :             //
     495           1 :             if(best_swapped > best)
     496             :             {
     497           2 :                 tags_table_t merged(*best_swapped_match);
     498           3 :                 merged.insert(
     499           2 :                           merged.end()
     500           2 :                         , t1->begin() + best_swapped
     501           5 :                         , t1->end());
     502           1 :                 intermediate_tags.push_back(merged);
     503             : 
     504           1 :                 processed_tags.insert(std::distance(f_tags.begin(), best_swapped_match));
     505             :             }
     506             :             else
     507             :             {
     508           0 :                 tags_table_t merged(*t1);
     509           0 :                 merged.insert(
     510           0 :                           merged.end()
     511           0 :                         , best_match->begin() + best
     512           0 :                         , best_match->end());
     513           0 :                 intermediate_tags.push_back(merged);
     514             : 
     515           0 :                 processed_tags.insert(std::distance(f_tags.begin(), best_match));
     516             :             }
     517             :         }
     518             :         else
     519             :         {
     520             :             // no merging possible, keep item as is for final
     521             :             //
     522         445 :             unhandled_tags.insert(std::distance(f_tags.begin(), t1));
     523             :         }
     524             :     }
     525             : 
     526             :     // repeat with the intermediate (which is unlikely to generate much
     527             :     // more merging, but we never know...)
     528             :     //
     529           1 :     bool repeat(false);
     530           1 :     do
     531             :     {
     532           1 :         repeat = false;
     533             : 
     534           2 :         for(std::size_t i1(0); i1 < intermediate_tags.size(); ++i1)
     535             :         {
     536           1 :             if(processed_intermediates.find(i1) != processed_intermediates.end())
     537             :             {
     538           0 :                 continue;
     539             :             }
     540             : 
     541           1 :             processed_intermediates.insert(i1);
     542             : 
     543           1 :             std::size_t best_intermediate_match(static_cast<std::size_t>(-1));
     544           1 :             std::size_t best(0);
     545           1 :             std::size_t best_swapped(0);
     546             : 
     547             :             // check against other unmerged tags
     548             :             //
     549           2 :             for(std::size_t i2(0); i2 < intermediate_tags.size(); ++i2)
     550             :             {
     551           1 :                 if(processed_intermediates.find(i2) != processed_intermediates.end())
     552             :                 {
     553             :                     // this was already used up, ignore
     554           1 :                     continue;
     555             :                 }
     556             : 
     557           0 :                 std::size_t const d1(end_start_match(intermediate_tags[i1], intermediate_tags[i2]));
     558           0 :                 std::size_t const d2(end_start_match(intermediate_tags[i2], intermediate_tags[i1]));
     559           0 :                 if(d2 > d1
     560           0 :                 && d2 > best_swapped)
     561             :                 {
     562           0 :                     best_swapped = d2;
     563           0 :                     best_intermediate_match = i2;
     564             :                 }
     565           0 :                 else if(d1 > best)
     566             :                 {
     567           0 :                     best = d1;
     568           0 :                     best_intermediate_match = i2;
     569             :                 }
     570             :             }
     571             : 
     572           1 :             if(best_intermediate_match != static_cast<std::size_t>(-1))
     573             :             {
     574           0 :                 repeat = true;
     575             : 
     576           0 :                 if(best_swapped > best)
     577             :                 {
     578           0 :                     tags_table_t merged(intermediate_tags[best_intermediate_match]);
     579           0 :                     merged.insert(
     580           0 :                               merged.end()
     581           0 :                             , intermediate_tags[i1].begin() + best_swapped
     582           0 :                             , intermediate_tags[i1].end());
     583           0 :                     intermediate_tags.push_back(merged);
     584             :                 }
     585             :                 else
     586             :                 {
     587           0 :                     tags_table_t merged(intermediate_tags[i1]);
     588           0 :                     merged.insert(
     589           0 :                               merged.end()
     590           0 :                             , intermediate_tags[best_intermediate_match].begin() + best
     591           0 :                             , intermediate_tags[best_intermediate_match].end());
     592           0 :                     intermediate_tags.push_back(merged);
     593             :                 }
     594             :             }
     595             :             else
     596             :             {
     597             :                 // no merging possible, keep item as is for now
     598             :                 //
     599           1 :                 unhandled_intermediates.insert(i1);
     600             :             }
     601             :         }
     602             :     }
     603             :     while(repeat);
     604             : 
     605             :     // once done merging, we end up with a set of tables which we can
     606             :     // merge all together and any tag table can then be found in this
     607             :     // final super-table
     608             :     //
     609         446 :     for(auto const & idx : unhandled_tags)
     610             :     {
     611         890 :         f_merged_tags.insert(
     612         890 :                   f_merged_tags.end()
     613         445 :                 , f_tags[idx].begin()
     614        2225 :                 , f_tags[idx].end());
     615             :     }
     616             : 
     617           2 :     for(auto const & idx : unhandled_intermediates)
     618             :     {
     619           2 :         f_merged_tags.insert(
     620           2 :                   f_merged_tags.end()
     621           1 :                 , intermediate_tags[idx].begin()
     622           5 :                 , intermediate_tags[idx].end());
     623             :     }
     624           1 : }
     625             : 
     626             : 
     627           2 : tld_tag_manager::tags_table_t const & tld_tag_manager::merged_tags() const
     628             : {
     629           2 :     return f_merged_tags;
     630             : }
     631             : 
     632             : 
     633           0 : std::size_t tld_tag_manager::merged_size() const
     634             : {
     635           0 :     return f_merged_tags.size();
     636             : }
     637             : 
     638             : 
     639       10464 : std::size_t tld_tag_manager::get_tag_offset(tags_t const & tags) const
     640             : {
     641       20928 :     tags_table_t const table(tags_to_table(tags));
     642       10464 :     auto it(std::search(
     643             :             f_merged_tags.begin(), f_merged_tags.end(),
     644       10464 :             table.begin(), table.end()));
     645       10464 :     if(it == f_merged_tags.end())
     646             :     {
     647           0 :         throw std::logic_error("tags not found in the list of merged tags.");
     648             :     }
     649             : 
     650       20928 :     return std::distance(f_merged_tags.begin(), it);
     651             : }
     652             : 
     653             : 
     654       20928 : tld_tag_manager::tags_table_t tld_tag_manager::tags_to_table(tags_t const & tags) const
     655             : {
     656       20928 :     tld_tag_manager::tags_table_t table;
     657       59822 :     for(auto const & t : tags)
     658             :     {
     659       38894 :         table.push_back(t.first);
     660       38894 :         table.push_back(t.second);
     661             :     }
     662       20928 :     return table;
     663             : }
     664             : 
     665             : 
     666      198980 : std::size_t tld_tag_manager::end_start_match(tags_table_t const & tag1, tags_table_t const & tag2)
     667             : {
     668      804797 :     for(std::string::size_type max(std::min(tag1.size(), tag2.size()) - 1);
     669      804797 :         max > 0;
     670             :         --max)
     671             :     {
     672      605818 :         if(std::equal(tag1.end() - max, tag1.end(), tag2.begin()))
     673             :         {
     674           1 :             return max;
     675             :         }
     676             :     }
     677             : 
     678      198979 :     return 0;
     679             : }
     680             : 
     681             : 
     682             : 
     683             : 
     684             : 
     685             : 
     686             : 
     687             : 
     688             : 
     689             : 
     690             : 
     691             : 
     692             : 
     693             : 
     694             : 
     695       10464 : tld_definition::tld_definition(tld_string_manager & strings)
     696       10464 :     : f_strings(strings)
     697             : {
     698       10464 : }
     699             : 
     700             : 
     701       22693 : bool tld_definition::add_segment(
     702             :           std::string const & segment
     703             :         , std::string & errmsg)
     704             : {
     705       22693 :     if((f_set & SET_TLD) != 0)
     706             :     {
     707           0 :         errmsg = "the TLD cannot be edited anymore (cannot add \""
     708           0 :                + segment
     709           0 :                + "\" to \""
     710           0 :                + get_name()
     711           0 :                + "\").";
     712           0 :         return false;
     713             :     }
     714             :     // f_set |= SET_TLD; -- reset_set_flags() sets this one
     715             : 
     716       22693 :     if(segment.empty())
     717             :     {
     718           0 :         errmsg = "a TLD segment cannot be an empty string.";
     719           0 :         return false;
     720             :     }
     721             : 
     722       45386 :     if(segment.front() == '-'
     723       22693 :     || segment.back() == '-')
     724             :     {
     725           0 :         errmsg = "a TLD segment (\""
     726           0 :                + segment
     727           0 :                + "\") cannot start or end with a dash ('-').";
     728           0 :         return false;
     729             :     }
     730             : 
     731       45386 :     std::string normalized_segment;
     732      123842 :     for(auto const & c : segment)
     733             :     {
     734      101149 :         switch(c)
     735             :         {
     736          85 :         case '*':
     737          85 :             if(segment.length() != 1)
     738             :             {
     739           0 :                 errmsg = "a TLD segment (\""
     740           0 :                        + segment
     741           0 :                        + "\") cannot include an asterisk character ('*')."
     742             :                          " However, the whole segment may be \"*\".";
     743           0 :                 return false;
     744             :             }
     745             :             [[fallthrough]];
     746             :         case '-':
     747             :         case '0':
     748             :         case '1':
     749             :         case '2':
     750             :         case '3':
     751             :         case '4':
     752             :         case '5':
     753             :         case '6':
     754             :         case '7':
     755             :         case '8':
     756             :         case '9':
     757        1768 :             normalized_segment += c;
     758        1768 :             break;
     759             : 
     760       99381 :         default:
     761       99381 :             if((c >= 'a' && c <= 'z')
     762        2854 :             || (c >= 'A' && c <= 'Z'))
     763             :             {
     764       96527 :                 normalized_segment += c;
     765             :             }
     766        2854 :             else if(static_cast<unsigned char>(c) < 0x80)
     767             :             {
     768           0 :                 if(static_cast<unsigned char>(c) < 0x20)
     769             :                 {
     770           0 :                     errmsg = "this TLD segment: \""
     771           0 :                            + segment
     772           0 :                            + "\" includes control character: '^"
     773           0 :                            + static_cast<char>(c + '@')
     774           0 :                            + "'.";
     775             :                 }
     776           0 :                 else if(c == 0x7F)
     777             :                 {
     778           0 :                     errmsg = "this TLD segment: \""
     779           0 :                            + segment
     780           0 :                            + "\" includes the delete character (0x7F).";
     781             :                 }
     782           0 :                 else if(static_cast<unsigned char>(c) >= 0x80 && static_cast<unsigned char>(c) < 0xA0)
     783             :                 {
     784           0 :                     errmsg = "this TLD segment: \""
     785           0 :                            + segment
     786           0 :                            + "\" includes graphic control character: '@"
     787           0 :                            + static_cast<char>(c - '@')
     788           0 :                            + "'.";
     789             :                 }
     790             :                 else
     791             :                 {
     792           0 :                     errmsg = "this TLD segment: \""
     793           0 :                            + segment
     794           0 :                            + "\" includes unsupported character: '"
     795           0 :                            + c
     796           0 :                            + "'.";
     797             :                 }
     798           0 :                 return false;
     799             :             }
     800             :             else
     801             :             {
     802             :                 // transform anything else in a %XX notation which is what
     803             :                 // is expected in a TLD reaching a server
     804             :                 //
     805        5708 :                 std::stringstream ss;
     806        2854 :                 ss << '%'
     807        2854 :                    << std::hex
     808             :                    << std::setw(2)
     809        2854 :                    << std::setfill('0')
     810        2854 :                    << static_cast<int>(static_cast<unsigned char>(c));
     811        2854 :                 normalized_segment += ss.str();
     812             :             }
     813       99381 :             break;
     814             : 
     815             :         }
     816             :     }
     817             : 
     818       22693 :     f_tld.push_back(f_strings.add_string(normalized_segment));
     819             : 
     820       22693 :     return true;
     821             : }
     822             : 
     823             : 
     824      156959 : tld_definition::segments_t const & tld_definition::get_segments() const
     825             : {
     826      156959 :     return f_tld;
     827             : }
     828             : 
     829             : 
     830             : /** \brief The domain name with periods separating each segment.
     831             :  *
     832             :  * This function rebuilds the full domain name. The idea is to have a way
     833             :  * to write error messages about various errors including the domain name.
     834             :  *
     835             :  * \return The name of the domain with each segment separated by periods.
     836             :  */
     837      102057 : std::string tld_definition::get_name() const
     838             : {
     839      102057 :     std::string name;
     840             : 
     841      319053 :     for(auto const & segment : f_tld)
     842             :     {
     843      433992 :         std::string const s(f_strings.get_string(segment));
     844      216996 :         if(s.empty())
     845             :         {
     846           0 :             throw std::logic_error("a segment string is not defined");
     847             :         }
     848      216996 :         name += '.';
     849      216996 :         name += s;
     850             :     }
     851             : 
     852      102057 :     return name;
     853             : }
     854             : 
     855             : 
     856             : /** \brief Get the full TLD as a reversed domain name.
     857             :  *
     858             :  * This function re-assembles the domain segments in a full name in reverse
     859             :  * order. This is used to properly sort sub-domain names (still part of the
     860             :  * TLD) by their parent domain name.
     861             :  *
     862             :  * We use '!' as the separate instead of the '.' because some domain names
     863             :  * have a dash in their as the order still needs to be correct and '.' > '-'
     864             :  * when we need the opposite, but '!' < '-'.
     865             :  *
     866             :  * \return The concatenated domain name in reverse order with '!' as separators.
     867             :  */
     868       10464 : std::string tld_definition::get_inverted_name() const
     869             : {
     870       10464 :     std::string name;
     871             : 
     872       33157 :     for(auto it(f_tld.rbegin()); it != f_tld.rend(); ++it)
     873             :     {
     874       45386 :         std::string const s(f_strings.get_string(*it));
     875       22693 :         if(s.empty())
     876             :         {
     877           0 :             throw std::logic_error("a segment string is not defined");
     878             :         }
     879       22693 :         name += '!';
     880       22693 :         name += s;
     881             :     }
     882             : 
     883       10464 :     return name;
     884             : }
     885             : 
     886             : 
     887           0 : std::string tld_definition::get_parent_name() const
     888             : {
     889           0 :     std::string name;
     890             : 
     891           0 :     bool skip_first(true);
     892           0 :     for(auto const & segment : f_tld)
     893             :     {
     894           0 :         std::string const s(f_strings.get_string(segment));
     895           0 :         if(s.empty())
     896             :         {
     897           0 :             throw std::logic_error("a segment string is not defined");
     898             :         }
     899           0 :         if(skip_first)
     900             :         {
     901           0 :             skip_first = false;
     902             :         }
     903             :         else
     904             :         {
     905           0 :             name += '.';
     906           0 :             name += s;
     907             :         }
     908             :     }
     909             : 
     910           0 :     return name;
     911             : }
     912             : 
     913             : 
     914       10464 : std::string tld_definition::get_parent_inverted_name() const
     915             : {
     916       10464 :     std::string name;
     917             : 
     918       22693 :     for(std::size_t idx(f_tld.size() - 1); idx > 0; --idx)
     919             :     {
     920       24458 :         std::string const s(f_strings.get_string(f_tld[idx]));
     921       12229 :         if(s.empty())
     922             :         {
     923           0 :             throw std::logic_error("a segment string is not defined");
     924             :         }
     925       12229 :         name += '!';
     926       12229 :         name += s;
     927             :     }
     928             : 
     929       10464 :     return name;
     930             : }
     931             : 
     932             : 
     933       10464 : void tld_definition::set_index(int idx)
     934             : {
     935       10464 :     f_index = idx;
     936       10464 : }
     937             : 
     938             : 
     939   109505781 : int tld_definition::get_index() const
     940             : {
     941   109505781 :     return f_index;
     942             : }
     943             : 
     944             : 
     945         547 : bool tld_definition::set_status(tld_status status)
     946             : {
     947         547 :     if((f_set & SET_STATUS) != 0)
     948             :     {
     949           0 :         return false;
     950             :     }
     951         547 :     f_set |= SET_STATUS;
     952             : 
     953         547 :     f_status = status;
     954             : 
     955         547 :     return true;
     956             : }
     957             : 
     958             : 
     959       31371 : tld_status tld_definition::get_status() const
     960             : {
     961       31371 :     return f_status;
     962             : }
     963             : 
     964             : 
     965          21 : bool tld_definition::set_apply_to(std::string const & apply_to)
     966             : {
     967          21 :     if((f_set & SET_APPLY_TO) != 0)
     968             :     {
     969           0 :         return false;
     970             :     }
     971          21 :     f_set |= SET_APPLY_TO;
     972             : 
     973          21 :     if(!apply_to.empty())
     974             :     {
     975          21 :         if(apply_to[0] == '.')
     976             :         {
     977             :             // remove the introductory period if present
     978             :             //
     979          11 :             f_apply_to = apply_to.substr(1);
     980          11 :             return true;
     981             :         }
     982             :     }
     983          10 :     f_apply_to = apply_to;
     984             : 
     985          10 :     return true;
     986             : }
     987             : 
     988             : 
     989       41898 : std::string tld_definition::get_apply_to() const
     990             : {
     991       41898 :     return f_apply_to;
     992             : }
     993             : 
     994             : 
     995       19449 : void tld_definition::add_tag(
     996             :       std::string const & tag_name
     997             :     , std::string const & value
     998             :     , std::string & errmsg)
     999             : {
    1000       19449 :     if(tag_name.empty())
    1001             :     {
    1002           0 :         errmsg = "tag name cannot be empty.";
    1003           0 :         return;
    1004             :     }
    1005             : 
    1006       19449 :     f_tags[f_strings.add_string(tag_name)] = f_strings.add_string(value);
    1007             : }
    1008             : 
    1009             : 
    1010       62784 : tags_t const & tld_definition::get_tags() const
    1011             : {
    1012       62784 :     return f_tags;
    1013             : }
    1014             : 
    1015             : 
    1016       10464 : void tld_definition::reset_set_flags()
    1017             : {
    1018       10464 :     f_set = SET_TLD;
    1019       10464 : }
    1020             : 
    1021             : 
    1022         568 : void tld_definition::set_named_parameter(
    1023             :       std::string const & name
    1024             :     , std::string const & value
    1025             :     , std::string & errmsg)
    1026             : {
    1027         568 :     if(!name.empty())
    1028             :     {
    1029         568 :         switch(name[0])
    1030             :         {
    1031          21 :         case 'a':
    1032          21 :             if(name == "apply_to")
    1033             :             {
    1034          21 :                 if(!set_apply_to(value))
    1035             :                 {
    1036           0 :                     errmsg = "\"apply_to\" defined a second time (\"" + value + "\").";
    1037             :                 }
    1038          21 :                 return;
    1039             :             }
    1040           0 :             break;
    1041             : 
    1042         547 :         case 's':
    1043         547 :             if(name == "status")
    1044             :             {
    1045         547 :                 if(!value.empty())
    1046             :                 {
    1047         547 :                     tld_status status(TLD_STATUS_UNDEFINED);
    1048         547 :                     switch(value[0])
    1049             :                     {
    1050         186 :                     case 'd':
    1051         186 :                         if(value == "deprecated")
    1052             :                         {
    1053         186 :                             status = TLD_STATUS_DEPRECATED;
    1054             :                         }
    1055         186 :                         break;
    1056             : 
    1057          22 :                     case 'e':
    1058          22 :                         if(value == "example")
    1059             :                         {
    1060           1 :                             status = TLD_STATUS_EXAMPLE;
    1061             :                         }
    1062          21 :                         else if(value == "exception")
    1063             :                         {
    1064          21 :                             status = TLD_STATUS_EXCEPTION;
    1065             :                         }
    1066          22 :                         break;
    1067             : 
    1068           8 :                     case 'i':
    1069           8 :                         if(value == "infrastructure")
    1070             :                         {
    1071           8 :                             status = TLD_STATUS_INFRASTRUCTURE;
    1072             :                         }
    1073           8 :                         break;
    1074             : 
    1075          12 :                     case 'p':
    1076          12 :                         if(value == "proposed")
    1077             :                         {
    1078          12 :                             status = TLD_STATUS_PROPOSED;
    1079             :                         }
    1080          12 :                         break;
    1081             : 
    1082          16 :                     case 'r':
    1083          16 :                         if(value == "reserved")
    1084             :                         {
    1085          16 :                             status = TLD_STATUS_RESERVED;
    1086             :                         }
    1087          16 :                         break;
    1088             : 
    1089           0 :                     case 'v':
    1090           0 :                         if(value == "valid")
    1091             :                         {
    1092           0 :                             status = TLD_STATUS_VALID;
    1093             :                         }
    1094           0 :                         break;
    1095             : 
    1096         303 :                     case 'u':
    1097         303 :                         if(value == "unused")
    1098             :                         {
    1099         303 :                             status = TLD_STATUS_UNUSED;
    1100             :                         }
    1101         303 :                         break;
    1102             : 
    1103             :                     }
    1104         547 :                     if(status != TLD_STATUS_UNDEFINED)
    1105             :                     {
    1106         547 :                         if(!set_status(status))
    1107             :                         {
    1108           0 :                             errmsg = "\"status\" defined a second time (\"" + value + "\").";
    1109             :                         }
    1110         547 :                         return;
    1111             :                     }
    1112             :                 }
    1113           0 :                 errmsg = "unknown \"status\": \"" + value + "\".";
    1114           0 :                 return;
    1115           0 :             }
    1116           0 :             break;
    1117             : 
    1118             :         }
    1119             :     }
    1120             : 
    1121           0 :     errmsg = "unknown variable name \"" + name + "\".";
    1122             : }
    1123             : 
    1124             : 
    1125        8842 : void tld_definition::set_start_offset(uint16_t start)
    1126             : {
    1127        8842 :     if(f_start_offset == USHRT_MAX)
    1128             :     {
    1129         795 :         f_start_offset = start;
    1130             :     }
    1131        8842 : }
    1132             : 
    1133             : 
    1134        8842 : void tld_definition::set_end_offset(uint16_t end)
    1135             : {
    1136        8842 :     f_end_offset = end;
    1137        8842 : }
    1138             : 
    1139             : 
    1140       32982 : uint16_t tld_definition::get_start_offset() const
    1141             : {
    1142       32982 :     return f_start_offset;
    1143             : }
    1144             : 
    1145             : 
    1146       12054 : uint16_t tld_definition::get_end_offset() const
    1147             : {
    1148       12054 :     return f_end_offset;
    1149             : }
    1150             : 
    1151             : 
    1152             : 
    1153             : 
    1154             : 
    1155             : 
    1156             : 
    1157             : 
    1158             : 
    1159             : 
    1160             : 
    1161             : 
    1162             : 
    1163       76577 : tld_compiler::token::token(
    1164             :           std::string const & filename
    1165             :         , int line
    1166             :         , token_t tok
    1167       76577 :         , std::string const & value)
    1168             :     : f_filename(filename)
    1169             :     , f_line(line)
    1170             :     , f_token(tok)
    1171       76577 :     , f_value(value)
    1172             : {
    1173       76577 : }
    1174             : 
    1175             : 
    1176           0 : std::string const & tld_compiler::token::get_filename() const
    1177             : {
    1178           0 :     return f_filename;
    1179             : }
    1180             : 
    1181             : 
    1182           8 : tld_string_manager & tld_compiler::get_string_manager()
    1183             : {
    1184           8 :     return f_strings;
    1185             : }
    1186             : 
    1187             : 
    1188             : 
    1189           0 : int tld_compiler::token::get_line() const
    1190             : {
    1191           0 :     return f_line;
    1192             : }
    1193             : 
    1194             : 
    1195       96141 : tld_compiler::token_t tld_compiler::token::get_token() const
    1196             : {
    1197       96141 :     return f_token;
    1198             : }
    1199             : 
    1200             : 
    1201       29550 : std::string const & tld_compiler::token::get_value() const
    1202             : {
    1203       29550 :     return f_value;
    1204             : }
    1205             : 
    1206             : 
    1207             : 
    1208             : 
    1209             : 
    1210             : 
    1211             : 
    1212             : 
    1213             : 
    1214           1 : void tld_compiler::set_input_folder(std::string const & path)
    1215             : {
    1216           1 :     f_input_folder = path;
    1217           1 : }
    1218             : 
    1219             : 
    1220           0 : std::string const & tld_compiler::get_input_folder() const
    1221             : {
    1222           0 :     return f_input_folder;
    1223             : }
    1224             : 
    1225             : 
    1226           1 : void tld_compiler::set_output(std::string const & output)
    1227             : {
    1228           1 :     f_output = output;
    1229           1 : }
    1230             : 
    1231             : 
    1232           0 : std::string const & tld_compiler::get_output() const
    1233             : {
    1234           0 :     return f_output;
    1235             : }
    1236             : 
    1237             : 
    1238           1 : void tld_compiler::set_c_file(std::string const & filename)
    1239             : {
    1240           1 :     f_c_file = filename;
    1241           1 : }
    1242             : 
    1243             : 
    1244           0 : std::string const & tld_compiler::get_c_file() const
    1245             : {
    1246           0 :     return f_c_file;
    1247             : }
    1248             : 
    1249             : 
    1250           1 : bool tld_compiler::compile()
    1251             : {
    1252           1 :     find_files(f_input_folder);
    1253           1 :     if(get_errno() != 0)
    1254             :     {
    1255           0 :         return false;
    1256             :     }
    1257             : 
    1258           1 :     process_input_files();
    1259           1 :     if(get_errno() != 0)
    1260             :     {
    1261           0 :         return false;
    1262             :     }
    1263             : 
    1264           1 :     define_default_category();
    1265           1 :     if(get_errno() != 0)
    1266             :     {
    1267           0 :         return false;
    1268             :     }
    1269             : 
    1270             :     // the merge feature is going to add merged strings to the table which
    1271             :     // are not going to be found in the description tables, so here we want
    1272             :     // to save the total number of strings prior to the merge process
    1273             :     //
    1274           1 :     f_strings_count = static_cast<string_id_t>(f_strings.size());
    1275             : 
    1276           1 :     f_strings.merge_strings();
    1277             : 
    1278           1 :     compress_tags();
    1279             : 
    1280           1 :     find_max_level();
    1281             : 
    1282           2 :     std::stringstream out;
    1283           1 :     output_tlds(out);
    1284           1 :     if(get_errno() != 0)
    1285             :     {
    1286           0 :         return false;
    1287             :     }
    1288             : 
    1289           1 :     save_to_file(out.str());    // save to tlds.tld (RIFF/TLDS format)
    1290           1 :     if(get_errno() != 0)
    1291             :     {
    1292           0 :         return false;
    1293             :     }
    1294             : 
    1295           1 :     save_to_c_file(out.str());
    1296           1 :     if(get_errno() != 0)
    1297             :     {
    1298           0 :         return false;
    1299             :     }
    1300             : 
    1301           1 :     return true;
    1302             : }
    1303             : 
    1304             : 
    1305       15292 : int tld_compiler::get_errno() const
    1306             : {
    1307       15292 :     return f_errno;
    1308             : }
    1309             : 
    1310             : 
    1311           0 : std::string const & tld_compiler::get_errmsg() const
    1312             : {
    1313           0 :     return f_errmsg;
    1314             : }
    1315             : 
    1316             : 
    1317           0 : int tld_compiler::get_line() const
    1318             : {
    1319           0 :     return f_line;
    1320             : }
    1321             : 
    1322             : 
    1323           0 : std::string const & tld_compiler::get_filename() const
    1324             : {
    1325           0 :     return f_filename;
    1326             : }
    1327             : 
    1328             : 
    1329           4 : void tld_compiler::find_files(std::string const & path)
    1330             : {
    1331           4 :     DIR * d = opendir(path.c_str());
    1332           4 :     if(d == nullptr)
    1333             :     {
    1334           0 :         f_errno = errno;
    1335           0 :         f_errmsg = "could not open directory \"" + path + "\".";
    1336           0 :         return;
    1337             :     }
    1338             :     // TODO: add `d` to a smart pointer
    1339             : 
    1340             :     for(;;)
    1341             :     {
    1342         282 :         struct dirent *e(readdir(d));
    1343         282 :         if(e == nullptr)
    1344             :         {
    1345           4 :             break;
    1346             :         }
    1347             : 
    1348         556 :         std::string name(e->d_name);
    1349         278 :         switch(e->d_type )
    1350             :         {
    1351          11 :         case DT_DIR:
    1352          11 :             if(strcmp(e->d_name, ".") != 0
    1353           7 :             && strcmp(e->d_name, "..") != 0)
    1354             :             {
    1355           3 :                 find_files(path + '/' + name);
    1356           3 :                 if(get_errno() != 0)
    1357             :                 {
    1358           0 :                     break;
    1359             :                 }
    1360             :             }
    1361          11 :             break;
    1362             : 
    1363         267 :         case DT_REG:
    1364             :         case DT_LNK:
    1365         534 :             if(name.length() > 4
    1366         267 :             && strcmp(name.c_str() + name.length() - 4, ".ini") == 0)
    1367             :             {
    1368             :                 // collect .ini files
    1369             :                 //
    1370         266 :                 f_input_files.push_back(path + '/' + name);
    1371             :             }
    1372         267 :             break;
    1373             : 
    1374           0 :         default:
    1375             :             // ignore other file types
    1376           0 :             break;
    1377             : 
    1378             :         }
    1379         278 :     }
    1380             : 
    1381           4 :     closedir(d);
    1382             : }
    1383             : 
    1384             : 
    1385           1 : void tld_compiler::process_input_files()
    1386             : {
    1387         267 :     for(auto const & filename : f_input_files)
    1388             :     {
    1389         266 :         process_file(filename);
    1390         266 :         if(get_errno() != 0)
    1391             :         {
    1392           0 :             return;
    1393             :         }
    1394             :     }
    1395             : }
    1396             : 
    1397             : 
    1398         266 : void tld_compiler::process_file(std::string const & filename)
    1399             : {
    1400         266 :     f_global_variables.clear();
    1401         266 :     f_global_tags.clear();
    1402         266 :     f_current_tld.clear();
    1403             : 
    1404         266 :     struct stat s;
    1405         266 :     int r(stat(filename.c_str(), &s));
    1406         266 :     if(r != 0)
    1407             :     {
    1408           0 :         f_errno = errno;
    1409           0 :         f_errmsg = "could not get statistics about \"" + filename + "\".";
    1410           0 :         return;
    1411             :     }
    1412         266 :     f_data.resize(s.st_size);
    1413             : 
    1414             :     {
    1415         532 :         std::ifstream in(filename);
    1416         266 :         in.read(reinterpret_cast<char *>(f_data.data()), f_data.size());
    1417         266 :         if(static_cast<size_t>(in.tellg()) != f_data.size())
    1418             :         {
    1419           0 :             f_errno = errno;
    1420           0 :             f_errmsg = "could not read file \"" + filename + "\" in full.";
    1421           0 :             return;
    1422             :         }
    1423             :     }
    1424             : 
    1425         266 :     f_pos = 0;
    1426         266 :     f_line = 1;
    1427         266 :     f_filename = filename;
    1428             :     for(;;)
    1429             :     {
    1430       29768 :         read_line();
    1431       15017 :         if(get_errno() != 0)
    1432             :         {
    1433           0 :             return;
    1434             :         }
    1435       15017 :         if(f_tokens.empty())
    1436             :         {
    1437        1232 :             continue;
    1438             :         }
    1439       27570 :         if(f_tokens.size() == 1
    1440       13785 :         && f_tokens[0].get_token() == TOKEN_EOF)
    1441             :         {
    1442         266 :             break;
    1443             :         }
    1444       13519 :         parse_line();
    1445             :     }
    1446             : }
    1447             : 
    1448             : 
    1449        1179 : bool tld_compiler::get_backslash(char32_t & c)
    1450             : {
    1451        1179 :     c = getc();
    1452        1179 :     if(c == CHAR_ERR)
    1453             :     {
    1454           0 :         return false;
    1455             :     }
    1456             : 
    1457        1179 :     int count(0);
    1458        1179 :     switch(c)
    1459             :     {
    1460           0 :     case CHAR_EOF:
    1461           0 :         c = '\\';
    1462           0 :         return true;
    1463             : 
    1464           0 :     case '\\':
    1465             :     case '\'':
    1466             :     case '"':
    1467             :     case ';':
    1468             :     case '#':
    1469             :     case '=':
    1470             :     case ':':
    1471           0 :         return true;
    1472             : 
    1473             :     // TODO: support octal
    1474             :     //
    1475           0 :     case '0': // null
    1476           0 :         c = 0x0;
    1477           0 :         return true;
    1478             : 
    1479           0 :     case 'a': // bell
    1480           0 :         c = 0x07;
    1481           0 :         return true;
    1482             : 
    1483           0 :     case 'b': // backspace
    1484           0 :         c = 0x08;
    1485           0 :         return true;
    1486             : 
    1487           0 :     case 't': // tab
    1488           0 :         c = 0x09;
    1489           0 :         return true;
    1490             : 
    1491           0 :     case 'f': // form feed
    1492           0 :         c = 0x0C;
    1493           0 :         return true;
    1494             : 
    1495           0 :     case 'r': // carriage return
    1496           0 :         c = 0x0D;
    1497           0 :         return true;
    1498             : 
    1499           0 :     case 'n': // line feed
    1500           0 :         c = 0x0A;
    1501           0 :         return true;
    1502             : 
    1503         233 :     case 'x':
    1504             :     case 'X':
    1505         233 :         count = 2;
    1506         233 :         break;
    1507             : 
    1508         946 :     case 'u':
    1509         946 :         count = 4;
    1510         946 :         break;
    1511             : 
    1512           0 :     case 'U':
    1513           0 :         count = 6; // in C/C++ this is 8
    1514           0 :         break;
    1515             : 
    1516             :     }
    1517             : 
    1518        1179 :     c = 0;
    1519        5423 :     for(int i(0); i < count; ++i)
    1520             :     {
    1521        4250 :         char32_t d(getc());
    1522        4250 :         if(d == CHAR_ERR)
    1523             :         {
    1524           0 :             f_errno = EINVAL;
    1525           0 :             f_errmsg = "unexpected error while reading escape Unicode character.";
    1526           0 :             return false;
    1527             :         }
    1528        4250 :         if(d == CHAR_EOF)
    1529             :         {
    1530           0 :             break;
    1531             :         }
    1532        4250 :         c <<= 4;
    1533        4250 :         if(d >= 'a' && d <= 'f')
    1534             :         {
    1535           0 :             c |= d - 'a' + 10;
    1536             :         }
    1537        4250 :         else if(d >= 'A' && d <= 'F')
    1538             :         {
    1539        1043 :             c |= d - 'A' + 10;
    1540             :         }
    1541        3207 :         else if(d >= '0' && d <= '9')
    1542             :         {
    1543        3201 :             c |= d - '0';
    1544             :         }
    1545             :         else
    1546             :         {
    1547           6 :             if(i == 0)
    1548             :             {
    1549           0 :                 f_errno = EINVAL;
    1550           0 :                 f_errmsg = "a Unicode character must include at least one hexdecimal digit.";
    1551           0 :                 return false;
    1552             :             }
    1553             : 
    1554             :             // premature end is okay by us
    1555             :             //
    1556           6 :             c >>= 4;        // cancel the shift
    1557           6 :             ungetc(d);
    1558           6 :             break;
    1559             :         }
    1560             :     }
    1561             : 
    1562        1179 :     return true;
    1563             : }
    1564             : 
    1565             : 
    1566       15017 : void tld_compiler::read_line()
    1567             : {
    1568       15017 :     f_tokens.clear();
    1569             : 
    1570             :     for(;;)
    1571             :     {
    1572       91922 :         char32_t c(getc());
    1573       91922 :         switch(c)
    1574             :         {
    1575           0 :         case CHAR_ERR:
    1576           0 :             return;
    1577             : 
    1578         266 :         case CHAR_EOF:
    1579         266 :             if(f_tokens.empty())
    1580             :             {
    1581         798 :                 f_tokens.emplace_back(
    1582             :                       f_filename
    1583             :                     , f_line
    1584             :                     , TOKEN_EOF
    1585        1064 :                     , std::string());
    1586             :             }
    1587         266 :             return;
    1588             : 
    1589           0 :         case '\r':
    1590           0 :             c = getc();
    1591           0 :             if(c == CHAR_ERR)
    1592             :             {
    1593           0 :                 return;
    1594             :             }
    1595           0 :             if(c != '\n')
    1596             :             {
    1597           0 :                 ungetc(c);
    1598             :             }
    1599           0 :             ++f_line;
    1600           0 :             return;
    1601             : 
    1602       14654 :         case '\n':
    1603       14654 :             ++f_line;
    1604       14654 :             return;
    1605             : 
    1606           0 :         case ';':
    1607             :             // "end of line" delimiter
    1608             :             // additional values etc. can appear after a semicolon
    1609           0 :             return;
    1610             : 
    1611        3055 :         case '=':
    1612        6110 :             f_tokens.emplace_back(
    1613             :                   f_filename
    1614             :                 , f_line
    1615             :                 , TOKEN_EQUAL
    1616        9165 :                 , "=");
    1617        3055 :             break;
    1618             : 
    1619       22704 :         case '.':
    1620       45408 :             f_tokens.emplace_back(
    1621             :                   f_filename
    1622             :                 , f_line
    1623             :                 , TOKEN_DOT
    1624       68112 :                 , ".");
    1625       22704 :             break;
    1626             : 
    1627          85 :         case '*':
    1628         170 :             f_tokens.emplace_back(
    1629             :                   f_filename
    1630             :                 , f_line
    1631             :                 , TOKEN_WILD_CARD
    1632         255 :                 , "*");
    1633          85 :             break;
    1634             : 
    1635           0 :         case '?':
    1636           0 :             f_tokens.emplace_back(
    1637             :                   f_filename
    1638             :                 , f_line
    1639             :                 , TOKEN_EXCEPTION
    1640           0 :                 , "?");
    1641           0 :             break;
    1642             : 
    1643       10464 :         case '[':
    1644       20928 :             f_tokens.emplace_back(
    1645             :                   f_filename
    1646             :                 , f_line
    1647             :                 , TOKEN_OPEN_SQUARE_BRACKET
    1648       31392 :                 , "[");
    1649       10464 :             break;
    1650             : 
    1651       10464 :         case ']':
    1652       20928 :             f_tokens.emplace_back(
    1653             :                   f_filename
    1654             :                 , f_line
    1655             :                 , TOKEN_CLOSE_SQUARE_BRACKET
    1656       31392 :                 , "]");
    1657       10464 :             break;
    1658             : 
    1659        4147 :         case '#':
    1660             :             for(;;)
    1661             :             {
    1662        8197 :                 c = getc();
    1663        4147 :                 switch(c)
    1664             :                 {
    1665           0 :                 case CHAR_ERR:
    1666             :                 case CHAR_EOF:
    1667           0 :                     return;
    1668             : 
    1669           0 :                 case L'\r':
    1670           0 :                     c = getc();
    1671           0 :                     if(c != L'\n')
    1672             :                     {
    1673           0 :                         ungetc(c);
    1674             :                     }
    1675           0 :                     ++f_line;
    1676           0 :                     return;
    1677             : 
    1678          97 :                 case L'\n':
    1679          97 :                     ++f_line;
    1680          97 :                     return;
    1681             : 
    1682             :                 }
    1683             :             }
    1684             :             break;
    1685             : 
    1686         637 :         case '"':
    1687             :         case '\'':
    1688             :             {
    1689         637 :                 int start_line(f_line);
    1690         637 :                 char32_t quote(c);
    1691             : 
    1692        1274 :                 std::string value;
    1693             :                 for(;;)
    1694             :                 {
    1695       23427 :                     c = getc();
    1696       12032 :                     if(c == CHAR_ERR)
    1697             :                     {
    1698           0 :                         return;
    1699             :                     }
    1700       12032 :                     if(c == CHAR_EOF)
    1701             :                     {
    1702           0 :                         f_errno = EINVAL;
    1703           0 :                         f_errmsg = "missing closing quote (";
    1704           0 :                         f_errmsg += static_cast<char>(quote);
    1705           0 :                         f_errmsg += ") for string.";
    1706           0 :                         return;
    1707             :                     }
    1708       12032 :                     if(c == quote)
    1709             :                     {
    1710         637 :                         break;
    1711             :                     }
    1712       11395 :                     if(c == '\\')
    1713             :                     {
    1714           9 :                         if(!get_backslash(c))
    1715             :                         {
    1716           0 :                             return;
    1717             :                         }
    1718             :                     }
    1719       11395 :                     if(!append_wc(value, c))
    1720             :                     {
    1721           0 :                         return;
    1722             :                     }
    1723             :                 }
    1724             : 
    1725        1274 :                 f_tokens.emplace_back(
    1726             :                       f_filename
    1727             :                     , start_line
    1728             :                     , TOKEN_STRING
    1729        2548 :                     , value);
    1730             :             }
    1731         637 :             break;
    1732             : 
    1733          38 :         case '0':
    1734             :         case '1':
    1735             :         case '2':
    1736             :         case '3':
    1737             :         case '4':
    1738             :         case '5':
    1739             :         case '6':
    1740             :         case '7':
    1741             :         case '8':
    1742             :         case '9':
    1743             :             {
    1744          76 :                 std::string value;
    1745          38 :                 value += static_cast<char>(c);
    1746             : 
    1747             :                 for(;;)
    1748             :                 {
    1749          76 :                     c = getc();
    1750          57 :                     if(c == CHAR_ERR)
    1751             :                     {
    1752           0 :                         return;
    1753             :                     }
    1754          57 :                     if(c < '0' || c > '9')
    1755             :                     {
    1756             :                         break;
    1757             :                     }
    1758          19 :                     value += static_cast<char>(c);
    1759             :                 }
    1760          38 :                 ungetc(c);
    1761             : 
    1762          76 :                 f_tokens.emplace_back(
    1763             :                       f_filename
    1764             :                     , f_line
    1765             :                     , TOKEN_NUMBER
    1766         152 :                     , value);
    1767             :             }
    1768          38 :             break;
    1769             : 
    1770       29458 :         default:
    1771       29458 :             if(is_space(c))
    1772             :             {
    1773             :                 // ignore spaces
    1774         594 :                 break;
    1775             :             }
    1776             : 
    1777       28864 :             if((c >= 'A' && c <= 'Z')
    1778       28864 :             || (c >= 'a' && c <= 'z')
    1779        1119 :             || c == '_')
    1780             :             {
    1781             :                 // identifier
    1782             :                 //
    1783       27745 :                 std::string value;
    1784       27745 :                 value += static_cast<char>(c);
    1785             :                 for(;;)
    1786             :                 {
    1787      240223 :                     c = getc();
    1788      133984 :                     if(c == CHAR_ERR)
    1789             :                     {
    1790           0 :                         return;
    1791             :                     }
    1792      133984 :                     if((c < 'A' || c > 'Z')
    1793      133984 :                     && (c < 'a' || c > 'z')
    1794       30830 :                     && (c < '0' || c > '9')
    1795       30255 :                     && c != '_'
    1796       30232 :                     && c != '/')
    1797             :                     {
    1798       27745 :                         break;
    1799             :                     }
    1800      106239 :                     value += static_cast<char>(c);
    1801             :                 }
    1802       27745 :                 if(!is_space(c))
    1803             :                 {
    1804       27745 :                     ungetc(c);
    1805             :                 }
    1806             : 
    1807       55490 :                 f_tokens.emplace_back(
    1808             :                       f_filename
    1809             :                     , f_line
    1810             :                     , TOKEN_IDENTIFIER
    1811       83235 :                     , value);
    1812       27745 :                 break;
    1813        1119 :             }
    1814             : 
    1815             :             // invalid character (mainly controls)
    1816             :             //
    1817        1119 :             if(c < 0x20                  // controls
    1818        1119 :             || (c >= 0x7F && c <= 0x9F)) // delete & graphic controls
    1819             :             {
    1820           0 :                 f_errno = EINVAL;
    1821           0 :                 f_errmsg = "unexpected character found '";
    1822           0 :                 if(c < 0x20)
    1823             :                 {
    1824           0 :                     f_errmsg += '^';
    1825           0 :                     f_errmsg += static_cast<char>(c + '@');
    1826             :                 }
    1827           0 :                 else if(c == 0x7F)
    1828             :                 {
    1829           0 :                     f_errmsg += "<DEL>";
    1830             :                 }
    1831             :                 else
    1832             :                 {
    1833           0 :                     f_errmsg += '@';
    1834           0 :                     f_errmsg += static_cast<char>(c - '@');
    1835             :                 }
    1836           0 :                 f_errmsg += "'.";
    1837           0 :                 return;
    1838             :             }
    1839             : 
    1840             :             {
    1841             :                 // anything else represents a "word"
    1842             :                 //
    1843        2238 :                 std::string value;
    1844             :                 for(;;)
    1845             :                 {
    1846       12209 :                     if(c == '\\')
    1847             :                     {
    1848        1170 :                         if(!get_backslash(c))
    1849             :                         {
    1850           0 :                             return;
    1851             :                         }
    1852             :                     }
    1853        6664 :                     if(!append_wc(value, c))
    1854             :                     {
    1855           0 :                         return;
    1856             :                     }
    1857             : 
    1858        6664 :                     c = getc();
    1859        6664 :                     if(c == CHAR_ERR)
    1860             :                     {
    1861           0 :                         return;
    1862             :                     }
    1863       13328 :                     if(c == CHAR_EOF
    1864        6664 :                     || is_space(c))
    1865             :                     {
    1866           0 :                         break;
    1867             :                     }
    1868        6664 :                     if(c == '.'
    1869        5741 :                     || c == '['
    1870        5741 :                     || c == '='
    1871        5741 :                     || c == ']')
    1872             :                     {
    1873        1119 :                         ungetc(c);
    1874        1119 :                         break;
    1875             :                     }
    1876             :                 }
    1877             : 
    1878        2238 :                 f_tokens.emplace_back(
    1879             :                       f_filename
    1880             :                     , f_line
    1881             :                     , TOKEN_WORD
    1882        4476 :                     , value);
    1883             :             }
    1884        1119 :             break;
    1885             : 
    1886             :         }
    1887       76905 :     }
    1888             : }
    1889             : 
    1890             : 
    1891             : 
    1892       63867 : bool tld_compiler::is_space(char32_t wc) const
    1893             : {
    1894       63867 :     if(wc == '\r'
    1895       63867 :     || wc == '\n')
    1896             :     {
    1897        2418 :         return false;
    1898             :     }
    1899             : 
    1900       61449 :     return iswspace(wc);
    1901             : }
    1902             : 
    1903             : 
    1904      254235 : char32_t tld_compiler::getc()
    1905             : {
    1906      254235 :     if(f_ungetc_pos > 0)
    1907             :     {
    1908       28908 :         --f_ungetc_pos;
    1909       28908 :         return f_ungetc[f_ungetc_pos];
    1910             :     }
    1911             : 
    1912      225327 :     if(f_pos >= f_data.size())
    1913             :     {
    1914         266 :         return CHAR_EOF;
    1915             :     }
    1916             : 
    1917      225061 :     int c(f_data[f_pos]);
    1918      225061 :     ++f_pos;
    1919             : 
    1920      225061 :     if(c < 0x80)
    1921             :     {
    1922      225061 :         return static_cast<char32_t>(c);
    1923             :     }
    1924             : 
    1925           0 :     char32_t wc(L'\0');
    1926           0 :     int cnt(0);
    1927           0 :     if(c >= 0xF0)
    1928             :     {
    1929           0 :         if(c >= 0xF8)
    1930             :         {
    1931           0 :             return CHAR_ERR;
    1932             :         }
    1933           0 :         wc = c & 0x07;
    1934           0 :         cnt = 3;
    1935             :     }
    1936           0 :     else if(c >= 0xE0)
    1937             :     {
    1938           0 :         wc = c & 0x0F;
    1939           0 :         cnt = 2;
    1940             :     }
    1941           0 :     else if(c >= 0xC0)
    1942             :     {
    1943           0 :         wc = c & 0x1F;
    1944           0 :         cnt = 1;
    1945             :     }
    1946             :     else
    1947             :     {
    1948           0 :         return CHAR_ERR;
    1949             :     }
    1950             : 
    1951           0 :     for(; cnt > 0; --cnt)
    1952             :     {
    1953           0 :         c = f_data[f_pos];
    1954           0 :         if(c == '\0')
    1955             :         {
    1956           0 :             return CHAR_ERR;
    1957             :         }
    1958           0 :         if(c < 0x80 || c > 0xBF)
    1959             :         {
    1960           0 :             return CHAR_ERR;
    1961             :         }
    1962           0 :         wc = (wc << 6) | (c & 0x3F);
    1963             :     }
    1964             : 
    1965           0 :     return wc;
    1966             : }
    1967             : 
    1968             : 
    1969       28908 : void tld_compiler::ungetc(char32_t c)
    1970             : {
    1971       28908 :     if(c == CHAR_EOF
    1972       28908 :     || c == CHAR_ERR)
    1973             :     {
    1974           0 :         return;
    1975             :     }
    1976             : 
    1977       28908 :     if(f_ungetc_pos >= std::size(f_ungetc))
    1978             :     {
    1979           0 :         throw std::logic_error("f_ungetc buffer is full");
    1980             :     }
    1981             : 
    1982       28908 :     f_ungetc[f_ungetc_pos] = c;
    1983       28908 :     ++f_ungetc_pos;
    1984             : }
    1985             : 
    1986             : 
    1987       18059 : bool tld_compiler::append_wc(std::string & value, char32_t wc)
    1988             : {
    1989       18059 :     if(wc < 0x80)
    1990             :     {
    1991       16880 :         value += static_cast<char>(wc);
    1992             :     }
    1993        1179 :     else if(wc < 0x800)
    1994             :     {
    1995         665 :         value += static_cast<char>(((wc >> 6) & 0x1F) | 0xC0);
    1996         665 :         value += static_cast<char>(((wc >> 0) & 0x3F) | 0x80);
    1997             :     }
    1998         514 :     else if(wc < 0x10000)
    1999             :     {
    2000         514 :         if(wc >= 0xD800 && wc <= 0xDFFF)
    2001             :         {
    2002             :             // you can't directly use a surrogate
    2003             :             //
    2004             :             // TODO: convert to hex number instead of base 10
    2005             :             //
    2006           0 :             f_errno = EINVAL;
    2007           0 :             f_errmsg = "trying to encode a surrogate Unicode code \""
    2008           0 :                      + std::to_string(static_cast<std::uint32_t>(wc))
    2009           0 :                      + "\" (base 10).";
    2010           0 :             return false;
    2011             :         }
    2012             : 
    2013         514 :         value += static_cast<char>(((wc >> 12) & 0x0F) | 0xE0);
    2014         514 :         value += static_cast<char>(((wc >>  6) & 0x3F) | 0x80);
    2015         514 :         value += static_cast<char>(((wc >>  0) & 0x3F) | 0x80);
    2016             :     }
    2017           0 :     else if(wc < 0x110000)
    2018             :     {
    2019           0 :         value += static_cast<char>(((wc >> 18) & 0x07) | 0xF0);
    2020           0 :         value += static_cast<char>(((wc >> 12) & 0x3F) | 0x80);
    2021           0 :         value += static_cast<char>(((wc >>  6) & 0x3F) | 0x80);
    2022           0 :         value += static_cast<char>(((wc >>  0) & 0x3F) | 0x80);
    2023             :     }
    2024           0 :     else if(wc != CHAR_EOF)
    2025             :     {
    2026             :         // TODO: convert to hex number instead of base 10
    2027             :         //
    2028           0 :         f_errno = EINVAL;
    2029           0 :         f_errmsg = "trying to encode invalid Unicode character \""
    2030           0 :                  + std::to_string(static_cast<std::uint32_t>(wc))
    2031           0 :                  + "\" (base 10).";
    2032           0 :         return false;
    2033             :     }
    2034             : 
    2035       18059 :     return true;
    2036             : }
    2037             : 
    2038             : 
    2039       13519 : void tld_compiler::parse_line()
    2040             : {
    2041       13519 :     switch(f_tokens[0].get_token())
    2042             :     {
    2043       10464 :     case TOKEN_OPEN_SQUARE_BRACKET:
    2044             :         // defining a new TLD
    2045             :         //
    2046       10464 :         parse_tld();
    2047       10464 :         break;
    2048             : 
    2049        3055 :     case TOKEN_IDENTIFIER:
    2050        3055 :         parse_variable();
    2051        3055 :         break;
    2052             : 
    2053           0 :     default:
    2054           0 :         f_errno = EINVAL;
    2055           0 :         f_errmsg = "invalid line, not recognized as a TLD definition nor a variable definition";
    2056             : //print_tokens();
    2057           0 :         break;
    2058             : 
    2059             :     }
    2060       13519 : }
    2061             : 
    2062             : 
    2063        3055 : void tld_compiler::parse_variable()
    2064             : {
    2065        6110 :     if(f_tokens.size() < 2
    2066        3055 :     || f_tokens[1].get_token() != TOKEN_EQUAL)
    2067             :     {
    2068           0 :         f_errno = EINVAL;
    2069           0 :         f_errmsg = "a variable name must be followed by an equal sign";
    2070           0 :         return;
    2071             :     }
    2072             : 
    2073        3055 :     std::string const & name(f_tokens[0].get_value());
    2074        3055 :     std::string::size_type const pos(name.find('/'));
    2075        3055 :     bool const is_tag(pos != std::string::npos);
    2076        3055 :     if(is_tag)
    2077             :     {
    2078        2487 :         if(name.substr(0, pos) != "tag")
    2079             :         {
    2080           0 :             f_errno = EINVAL;
    2081           0 :             f_errmsg = "variable name \""
    2082           0 :                 + name
    2083           0 :                 + "\" does not start with \"tag/...\".";
    2084           0 :             return;
    2085             :         }
    2086        2487 :         std::string::size_type const more(name.find('/', pos + 1));
    2087        2487 :         if(more != std::string::npos)
    2088             :         {
    2089           0 :             f_errno = EINVAL;
    2090           0 :             f_errmsg = "variable name \""
    2091           0 :                 + name
    2092           0 :                 + "\" cannot include more than one slash (/).";
    2093           0 :             return;
    2094             :         }
    2095             :     }
    2096             : 
    2097        6110 :     std::string value;
    2098        3055 :     if(f_tokens.size() > 3UL)
    2099             :     {
    2100             :         // we do not allow mixing words & strings in the value, so make
    2101             :         // sure that if we have more than 3 tokens, none at index
    2102             :         // 2+ are strings
    2103             :         //
    2104          34 :         for(std::size_t idx(2); idx < f_tokens.size(); ++idx)
    2105             :         {
    2106          23 :             if(f_tokens[idx].get_token() == TOKEN_STRING)
    2107             :             {
    2108           0 :                 f_errno = EINVAL;
    2109           0 :                 f_errmsg = "a variable value cannot mix words and a string";
    2110           0 :                 return;
    2111             :             }
    2112          23 :             if(idx != 2)
    2113             :             {
    2114          12 :                 value += ' ';
    2115             :             }
    2116          23 :             value = f_tokens[idx].get_value();
    2117             :         }
    2118             :     }
    2119        3044 :     else if(f_tokens.size() == 3)
    2120             :     {
    2121        3044 :         value = f_tokens[2].get_value();
    2122             :     }
    2123             : 
    2124        3055 :     if(is_tag)
    2125             :     {
    2126        4974 :         std::string const tag_name(name.substr(pos + 1));
    2127        2487 :         if(f_current_tld.empty())
    2128             :         {
    2129         281 :             f_global_tags[tag_name] = value;
    2130             :         }
    2131             :         else
    2132             :         {
    2133        2206 :             f_definitions[f_current_tld]->add_tag(tag_name, value, f_errmsg);
    2134        2206 :             if(!f_errmsg.empty())
    2135             :             {
    2136           0 :                 f_errno = EINVAL;
    2137           0 :                 return;
    2138             :             }
    2139             :         }
    2140             :     }
    2141             :     else
    2142             :     {
    2143         568 :         if(f_current_tld.empty())
    2144             :         {
    2145           0 :             if(f_global_variables.find(name) != f_global_variables.end())
    2146             :             {
    2147           0 :                 f_errno = EINVAL;
    2148           0 :                 f_errmsg = "\"" + name + "\" global variable defined more than once.";
    2149           0 :                 return;
    2150             :             }
    2151             : 
    2152             :             // name != "apply_to" -- I don't think that would be useful as a global
    2153           0 :             if(pos != std::string::npos     // any tag
    2154           0 :             && name != "status")
    2155             :             {
    2156           0 :                 f_errno = EINVAL;
    2157           0 :                 f_errmsg = "variable with name \"" + name + "\" is not supported. Missing \"tag/\"?";
    2158           0 :                 return;
    2159             :             }
    2160             : 
    2161           0 :             f_global_variables[name] = value;
    2162             :         }
    2163             :         else
    2164             :         {
    2165         568 :             f_definitions[f_current_tld]->set_named_parameter(name, value, f_errmsg);
    2166         568 :             if(!f_errmsg.empty())
    2167             :             {
    2168           0 :                 f_errno = EINVAL;
    2169           0 :                 return;
    2170             :             }
    2171             :         }
    2172             :     }
    2173             : }
    2174             : 
    2175             : 
    2176       10464 : void tld_compiler::parse_tld()
    2177             : {
    2178       10464 :     std::size_t const max(f_tokens.size() - 1);
    2179       10464 :     if(max < 2
    2180       10464 :     || f_tokens[max].get_token() != TOKEN_CLOSE_SQUARE_BRACKET)
    2181             :     {
    2182           0 :         f_errno = EINVAL;
    2183           0 :         f_errmsg = "a TLD must end with a closing square bracket (]) and not be empty";
    2184             : //print_tokens();
    2185           0 :         return;
    2186             :     }
    2187             : 
    2188       10464 :     std::size_t idx(1);
    2189             : 
    2190       10464 :     bool is_exception(false);
    2191       10464 :     if(f_tokens[idx].get_token() == TOKEN_EXCEPTION)
    2192             :     {
    2193           0 :         is_exception = true;
    2194           0 :         ++idx;
    2195             : 
    2196           0 :         if(idx >= max)
    2197             :         {
    2198           0 :             f_errno = EINVAL;
    2199           0 :             f_errmsg = "a TLD cannot just be an exception (?), a name is required";
    2200           0 :             return;
    2201             :         }
    2202             :     }
    2203             : 
    2204             :     // the very first dot is optional now
    2205             :     //
    2206       10464 :     if(f_tokens[idx].get_token() == TOKEN_DOT)
    2207             :     {
    2208       10464 :         ++idx;
    2209             : 
    2210       10464 :         if(idx >= max)
    2211             :         {
    2212           0 :             f_errno = EINVAL;
    2213           0 :             f_errmsg = "a TLD cannot just be a dot (?), a name is required";
    2214           0 :             return;
    2215             :         }
    2216             :     }
    2217             : 
    2218       20928 :     tld_definition::pointer_t tld(std::make_shared<tld_definition>(f_strings));
    2219             : 
    2220             :     // a TLD always starts with a dot, but we do not force the user to enter it
    2221             :     //
    2222             :     // TODO: keep the name separated (since we already cut it at the dots)
    2223             :     //
    2224             :     for(;;)
    2225             :     {
    2226       22693 :         switch(f_tokens[idx].get_token())
    2227             :         {
    2228           0 :         case TOKEN_DOT:
    2229           0 :             f_errno = EINVAL;
    2230           0 :             f_errmsg = "a TLD cannot include two dots (.) in a raw.";
    2231           0 :             return;
    2232             : 
    2233          85 :         case TOKEN_WILD_CARD:
    2234          85 :             if(!tld->add_segment("*", f_errmsg))
    2235             :             {
    2236           0 :                 f_errno = EINVAL;
    2237           0 :                 return;
    2238             :             }
    2239          85 :             ++idx;
    2240          85 :             break;
    2241             : 
    2242       22608 :         case TOKEN_IDENTIFIER:
    2243             :         case TOKEN_WORD:
    2244             :         case TOKEN_NUMBER:
    2245             :             {
    2246       45216 :                 std::string segment(f_tokens[idx].get_value());
    2247       22608 :                 bool found_dot(false);
    2248       22608 :                 ++idx;
    2249       48536 :                 while(idx < max && !found_dot)
    2250             :                 {
    2251       12964 :                     switch(f_tokens[idx].get_token())
    2252             :                     {
    2253         820 :                     case TOKEN_IDENTIFIER:
    2254             :                     case TOKEN_WORD:
    2255             :                     case TOKEN_NUMBER:
    2256         820 :                         segment += f_tokens[idx].get_value();
    2257         820 :                         ++idx;
    2258         820 :                         break;
    2259             : 
    2260       12144 :                     case TOKEN_DOT:
    2261       12144 :                         found_dot = true;
    2262       12144 :                         break;
    2263             : 
    2264           0 :                     default:
    2265           0 :                         f_errno = EINVAL;
    2266           0 :                         f_errmsg = "unexpected token in a TLD (strings and special characters are not allowed).";
    2267           0 :                         return;
    2268             : 
    2269             :                     }
    2270             :                 }
    2271       22608 :                 if(!tld->add_segment(segment, f_errmsg))
    2272             :                 {
    2273           0 :                     f_errno = EINVAL;
    2274           0 :                     return;
    2275       22608 :                 }
    2276             :             }
    2277       22608 :             break;
    2278             : 
    2279           0 :         default:
    2280           0 :             f_errno = EINVAL;
    2281           0 :             f_errmsg = "unexpected token in a TLD (strings and special characters are not allowed.)";
    2282           0 :             return;
    2283             : 
    2284             :         }
    2285             : 
    2286       22693 :         if(idx >= max)
    2287             :         {
    2288       10464 :             break;
    2289             :         }
    2290             : 
    2291       12229 :         if(f_tokens[idx].get_token() != TOKEN_DOT)
    2292             :         {
    2293           0 :             f_errno = EINVAL;
    2294           0 :             f_errmsg = "expected a dot (.) between TLD names";
    2295           0 :             return;
    2296             :         }
    2297       12229 :         ++idx;
    2298             : 
    2299       12229 :         if(idx >= max)
    2300             :         {
    2301             :             // allow ending names with a period (optional)
    2302             :             //
    2303           0 :             break;
    2304             :         }
    2305       12229 :     }
    2306             : 
    2307             :     // use the '!' (0x21) for sorting, because '.' (0x2E) is after '-' (0x2D)
    2308             :     // and there is no '!' allowed in domain names (so far)
    2309             :     //
    2310             :     // the get_inverted_name() takes care of that
    2311             :     //
    2312       10464 :     f_current_tld = tld->get_inverted_name();
    2313             : 
    2314       10464 :     if(f_definitions.find(f_current_tld) != f_definitions.end())
    2315             :     {
    2316           0 :         f_errno = EINVAL;
    2317           0 :         f_errmsg = "TLD name \""
    2318           0 :                  + tld->get_name()
    2319           0 :                  + "\" defined twice.";
    2320           0 :         return;
    2321             :     }
    2322             : 
    2323       10464 :     f_definitions[f_current_tld] = tld;
    2324             : 
    2325             :     // add the globals to this definition
    2326             :     //
    2327       10464 :     for(auto const & g : f_global_variables)
    2328             :     {
    2329           0 :         f_definitions[f_current_tld]->set_named_parameter(g.first, g.second, f_errmsg);
    2330           0 :         if(!f_errmsg.empty())
    2331             :         {
    2332             :             // this should not happen since the globals are defined in a map
    2333             :             //
    2334           0 :             f_errno = EINVAL;
    2335           0 :             return;
    2336             :         }
    2337             :     }
    2338             : 
    2339       21188 :     for(auto const & g : f_global_tags)
    2340             :     {
    2341       10724 :         f_definitions[f_current_tld]->add_tag(g.first, g.second, f_errmsg);
    2342       10724 :         if(!f_errmsg.empty())
    2343             :         {
    2344             :             // this should not happen since the globals are defined in a map
    2345             :             //
    2346           0 :             f_errno = EINVAL;
    2347           0 :             return;
    2348             :         }
    2349             :     }
    2350             : 
    2351       10464 :     f_definitions[f_current_tld]->reset_set_flags();
    2352             : }
    2353             : 
    2354             : 
    2355           0 : void tld_compiler::print_tokens()
    2356             : {
    2357           0 :     for(auto const & t : f_tokens)
    2358             :     {
    2359             :         std::cerr
    2360           0 :             << t.get_filename()
    2361           0 :             << ":"
    2362             :             << t.get_line()
    2363           0 :             << ": "
    2364           0 :             << static_cast<int>(t.get_token())
    2365             :             << " = \""
    2366           0 :             << t.get_value()
    2367           0 :             << "\"\n";
    2368             : 
    2369             :         //std::string const &     get_filename() const;
    2370             :         //int                     get_line() const;
    2371             :         //token_t                 get_token() const;
    2372             :         //std::string const &     get_value() const;
    2373             :     }
    2374           0 : }
    2375             : 
    2376             : 
    2377           1 : void tld_compiler::define_default_category()
    2378             : {
    2379           1 :     string_id_t const category_id(f_strings.add_string("category"));
    2380           1 :     string_id_t const country_id(f_strings.add_string("country"));
    2381             : 
    2382       10465 :     for(auto const & d : f_definitions)
    2383             :     {
    2384       10464 :         tags_t const & tags(d.second->get_tags());
    2385       10464 :         auto it(tags.find(category_id));
    2386       10464 :         if(it == tags.end())
    2387             :         {
    2388             :             // there is no category yet, let's determine that now
    2389             :             //
    2390        6519 :             if(tags.find(country_id) != tags.end())
    2391             :             {
    2392        6519 :                 d.second->add_tag("category", "country", f_errmsg);
    2393        6519 :                 if(!f_errmsg.empty())
    2394             :                 {
    2395           0 :                     f_errno = EINVAL;
    2396           0 :                     return;
    2397             :                 }
    2398             :             }
    2399             :             else
    2400             :             {
    2401           0 :                 f_errmsg = "domain \""
    2402           0 :                     + d.second->get_name()
    2403           0 :                     + "\" has no category and we had no way to determine a default category.";
    2404           0 :                 f_errno = EINVAL;
    2405           0 :                 return;
    2406             :             }
    2407             :         }
    2408             :     }
    2409             : }
    2410             : 
    2411             : 
    2412           1 : void tld_compiler::compress_tags()
    2413             : {
    2414       10465 :     for(auto const & d : f_definitions)
    2415             :     {
    2416       10464 :         f_tags.add(d.second->get_tags());
    2417             :     }
    2418             : 
    2419           1 :     f_tags.merge();
    2420           1 : }
    2421             : 
    2422             : 
    2423       10464 : uint16_t tld_compiler::find_definition(std::string name) const
    2424             : {
    2425       10464 :     if(!name.empty())
    2426             :     {
    2427          21 :         if(name[0] != '.')
    2428             :         {
    2429          21 :             name = '.' + name;
    2430             :         }
    2431       91593 :         for(auto const & it : f_definitions)
    2432             :         {
    2433       91593 :             if(it.second->get_name() == name)
    2434             :             {
    2435          21 :                 return it.second->get_index();
    2436             :             }
    2437             :         }
    2438             :     }
    2439             : 
    2440       10443 :     return USHRT_MAX;
    2441             : }
    2442             : 
    2443             : 
    2444             : /** \brief Determine the longest TLD in terms of levels.
    2445             :  * 
    2446             :  * This function searches all the definitions checking for the longest
    2447             :  * number of segments (which is the number of periods in the TLD including
    2448             :  * the starting period, so in ".com", we have a level of 1).
    2449             :  */
    2450           1 : void tld_compiler::find_max_level()
    2451             : {
    2452           1 :     f_tld_max_level = 0;
    2453             : 
    2454           1 :     auto it(std::max_element(
    2455             :               f_definitions.begin()
    2456             :             , f_definitions.end()
    2457       10463 :             , [](auto const & a, auto const & b)
    2458             :               {
    2459       10463 :                   return a.second->get_segments().size()
    2460       10463 :                                         < b.second->get_segments().size();
    2461       10464 :               }));
    2462           1 :     if(it == f_definitions.end())
    2463             :     {
    2464           0 :         f_errno = EINVAL;
    2465           0 :         f_errmsg = "error: could not find a definition with a larger level.";
    2466           0 :         return;
    2467             :     }
    2468             : 
    2469           1 :     f_tld_max_level = it->second->get_segments().size();
    2470             : }
    2471             : 
    2472             : 
    2473           1 : void tld_compiler::output_tlds(std::ostream & out)
    2474             : {
    2475             : #pragma GCC diagnostic push
    2476             : #pragma GCC diagnostic ignored "-Wpedantic"
    2477           1 :     tld_header header =
    2478             :     {
    2479             :         .f_version_major = 1,
    2480             :         .f_version_minor = 0,
    2481             :         .f_pad0 = 0,
    2482           1 :         .f_tld_max_level = f_tld_max_level,
    2483             :         .f_tld_start_offset = USHRT_MAX,
    2484             :         .f_tld_end_offset = USHRT_MAX,
    2485           1 :         .f_created_on = f_created_on,
    2486           2 :     };
    2487             : #pragma GCC diagnostic pop
    2488             : 
    2489             :     // define the "offsets" (indices) of all the items
    2490             :     //
    2491             :     // the index will be used for the `apply_to` below to properly
    2492             :     // determine the exception
    2493             :     //
    2494           1 :     int i(0);
    2495           6 :     for(uint8_t level(f_tld_max_level); level > 0; --level)
    2496             :     {
    2497       52325 :         for(auto const & d : f_definitions)
    2498             :         {
    2499       52320 :             if(d.second->get_segments().size() == level)
    2500             :             {
    2501       10464 :                 d.second->set_index(i);
    2502       10464 :                 ++i;
    2503             :             }
    2504             :         }
    2505             :     }
    2506             : 
    2507             :     // now we create the TLD table with the largest levels first,
    2508             :     // as we do so we save the index of the start and stop
    2509             :     // points of each level in the previous level (hence the
    2510             :     // need for a level 0 entry)
    2511             :     //
    2512             :     // we create the table in memory; we need the level 0 offsets in
    2513             :     // the header before we can start saving the results in the output
    2514             :     // file...
    2515             :     //
    2516           2 :     std::vector<tld_description> descriptions;
    2517           1 :     i = 0;
    2518           6 :     for(uint8_t level(header.f_tld_max_level); level > 0; --level)
    2519             :     {
    2520       52325 :         for(auto const & d : f_definitions)
    2521             :         {
    2522       52320 :             if(d.second->get_segments().size() == level)
    2523             :             {
    2524             : #pragma GCC diagnostic push
    2525             : #pragma GCC diagnostic ignored "-Wpedantic"
    2526       10464 :                 tld_description description =
    2527             :                 {
    2528             :                     // make sure it's set to exception if we have an "apply to"
    2529             :                     // (probably not required since we can check whether we do
    2530             :                     // have an apply to)
    2531             :                     //
    2532       20928 :                     .f_status = static_cast<uint8_t>(d.second->get_apply_to().empty()
    2533       10443 :                                     ? d.second->get_status()
    2534             :                                     : TLD_STATUS_EXCEPTION),
    2535             :                     .f_exception_level = level,
    2536       20928 :                     .f_exception_apply_to = find_definition(d.second->get_apply_to()),
    2537       10464 :                     .f_start_offset = d.second->get_start_offset(),
    2538       10464 :                     .f_end_offset = d.second->get_end_offset(),
    2539       10464 :                     .f_tld = static_cast<uint16_t>(d.second->get_segments()[0]),
    2540       10464 :                     .f_tags = static_cast<uint16_t>(f_tags.get_tag_offset(d.second->get_tags())),
    2541       10464 :                     .f_tags_count = static_cast<uint16_t>(d.second->get_tags().size()),
    2542      104619 :                 };
    2543             : #pragma GCC diagnostic pop
    2544             : 
    2545       20928 :                 std::string const parent_name(d.second->get_parent_inverted_name());
    2546       10464 :                 if(parent_name.empty())
    2547             :                 {
    2548        1622 :                     if(f_tld_start_offset == USHRT_MAX)
    2549             :                     {
    2550           1 :                         f_tld_start_offset = i;
    2551             :                     }
    2552        1622 :                     f_tld_end_offset = i + 1;
    2553             :                 }
    2554             :                 else
    2555             :                 {
    2556        8842 :                     auto it(f_definitions.find(parent_name));
    2557        8842 :                     if(it == f_definitions.end())
    2558             :                     {
    2559           0 :                         f_errno = EINVAL;
    2560           0 :                         f_errmsg = "parent domain \""
    2561           0 :                             + parent_name
    2562           0 :                             + "\" not found.";
    2563           0 :                         return;
    2564             :                     }
    2565        8842 :                     it->second->set_start_offset(i);
    2566        8842 :                     it->second->set_end_offset(i + 1);
    2567             :                 }
    2568             : 
    2569       10464 :                 descriptions.push_back(description);
    2570             : 
    2571       10464 :                 ++i;
    2572             :             }
    2573             :         }
    2574             :     }
    2575             : 
    2576           1 :     header.f_tld_start_offset = f_tld_start_offset;
    2577           1 :     header.f_tld_end_offset = f_tld_end_offset;
    2578             : 
    2579           1 :     tld_hunk header_hunk;
    2580           1 :     header_hunk.f_name = TLD_HEADER;
    2581           1 :     header_hunk.f_size = sizeof(tld_header);
    2582             : 
    2583           1 :     tld_hunk descriptions_hunk;
    2584           1 :     descriptions_hunk.f_name = TLD_DESCRIPTIONS;
    2585           1 :     descriptions_hunk.f_size = sizeof(tld_description) * f_definitions.size();
    2586             : 
    2587           1 :     tld_hunk tags_hunk;
    2588           1 :     tags_hunk.f_name = TLD_TAGS;
    2589           1 :     tags_hunk.f_size = f_tags.merged_tags().size() * sizeof(uint32_t); // NOT sizeof(tld_tags) because the merged vector is not one to one equivalent
    2590             : 
    2591           1 :     tld_hunk string_offsets_hunk;
    2592           1 :     string_offsets_hunk.f_name = TLD_STRING_OFFSETS;
    2593           1 :     string_offsets_hunk.f_size = static_cast<std::size_t>(f_strings_count) * sizeof(tld_string_offset);
    2594             : 
    2595           1 :     tld_hunk string_lengths_hunk;
    2596           1 :     string_lengths_hunk.f_name = TLD_STRING_LENGTHS;
    2597           1 :     string_lengths_hunk.f_size = static_cast<std::size_t>(f_strings_count) * sizeof(tld_string_length);
    2598             : 
    2599           1 :     tld_hunk strings_hunk;
    2600           1 :     strings_hunk.f_name = TLD_STRINGS;
    2601           1 :     strings_hunk.f_size = f_strings.compressed_length();
    2602             : 
    2603           1 :     tld_magic magic;
    2604           1 :     magic.f_riff = TLD_MAGIC;
    2605           1 :     magic.f_size = sizeof(magic.f_type)
    2606           1 :         + sizeof(tld_hunk) + header_hunk.f_size
    2607           1 :         + sizeof(tld_hunk) + descriptions_hunk.f_size
    2608           1 :         + sizeof(tld_hunk) + tags_hunk.f_size
    2609           1 :         + sizeof(tld_hunk) + string_offsets_hunk.f_size
    2610           1 :         + sizeof(tld_hunk) + string_lengths_hunk.f_size
    2611           1 :         + sizeof(tld_hunk) + strings_hunk.f_size;
    2612           1 :     magic.f_type = TLD_TLDS;
    2613             : 
    2614           1 :     out.write(reinterpret_cast<char const *>(&magic), sizeof(magic));
    2615             : 
    2616             :     // header
    2617             :     //
    2618           1 :     out.write(reinterpret_cast<char const *>(&header_hunk), sizeof(header_hunk));
    2619           1 :     out.write(reinterpret_cast<char const *>(&header), sizeof(header));
    2620             : 
    2621             :     // descriptions
    2622             :     //
    2623           1 :     out.write(reinterpret_cast<char const *>(&descriptions_hunk), sizeof(descriptions_hunk));
    2624           1 :     out.write(reinterpret_cast<char const *>(descriptions.data()), descriptions.size() * sizeof(tld_description));
    2625             : 
    2626             :     // tags
    2627             :     //
    2628           1 :     out.write(reinterpret_cast<char const *>(&tags_hunk), sizeof(tags_hunk));
    2629           1 :     out.write(reinterpret_cast<char const *>(f_tags.merged_tags().data()), tags_hunk.f_size);
    2630             : 
    2631             :     // strings: offsets
    2632             :     //
    2633           1 :     out.write(reinterpret_cast<char const *>(&string_offsets_hunk), sizeof(string_offsets_hunk));
    2634        7312 :     for(string_id_t idx(1); idx <= f_strings_count; ++idx)
    2635             :     {
    2636             : #pragma GCC diagnostic push
    2637             : #pragma GCC diagnostic ignored "-Wpedantic"
    2638        7311 :         tld_string_offset offset =
    2639             :         {
    2640        7311 :             .f_string_offset = static_cast<uint32_t>(f_strings.get_string_offset(idx)),
    2641        7311 :         };
    2642             : #pragma GCC diagnostic pop
    2643        7311 :         out.write(reinterpret_cast<char const *>(&offset), sizeof(offset));
    2644             :     }
    2645             : 
    2646             :     // strings: lengths
    2647             :     //
    2648           1 :     out.write(reinterpret_cast<char const *>(&string_lengths_hunk), sizeof(string_lengths_hunk));
    2649        7312 :     for(string_id_t idx(1); idx <= f_strings_count; ++idx)
    2650             :     {
    2651             : #pragma GCC diagnostic push
    2652             : #pragma GCC diagnostic ignored "-Wpedantic"
    2653        7311 :         tld_string_length length =
    2654             :         {
    2655       14622 :             .f_string_length = static_cast<uint16_t>(f_strings.get_string(idx).length()),
    2656       14622 :         };
    2657             : #pragma GCC diagnostic pop
    2658        7311 :         out.write(reinterpret_cast<char const *>(&length), sizeof(length));
    2659             :     }
    2660             : 
    2661             :     // strings: actual strings
    2662             :     //
    2663           1 :     out.write(reinterpret_cast<char const *>(&strings_hunk), sizeof(strings_hunk));
    2664           1 :     out.write(f_strings.compressed_strings().c_str(), strings_hunk.f_size);
    2665             : }
    2666             : 
    2667             : 
    2668           1 : void tld_compiler::save_to_file(std::string const & buffer)
    2669             : {
    2670           2 :     std::ofstream out;
    2671           1 :     out.open(f_output);
    2672           1 :     if(!out)
    2673             :     {
    2674           0 :         f_errno = errno;
    2675           0 :         f_errmsg = "error: could not open output file \""
    2676           0 :                  + f_output
    2677           0 :                  + "\", errno: "
    2678           0 :                  + std::to_string(f_errno)
    2679           0 :                  + ", "
    2680           0 :                  + strerror(f_errno)
    2681           0 :                  + ".";
    2682           0 :         return;
    2683             :     }
    2684             : 
    2685           1 :     out.write(buffer.c_str(), buffer.length());
    2686             : }
    2687             : 
    2688             : 
    2689           1 : void tld_compiler::output_header(std::ostream & out)
    2690             : {
    2691           1 :     time_t const now(time(nullptr));
    2692           1 :     struct tm t;
    2693           1 :     localtime_r(&now, &t);
    2694           1 :     char year[16];
    2695           1 :     strftime(year, sizeof(year), "%Y", &t);
    2696             : 
    2697           2 :     std::string basename;
    2698           1 :     std::string::size_type const pos(f_c_file.rfind('/'));
    2699           1 :     if(pos == std::string::npos)
    2700             :     {
    2701           0 :         basename = f_c_file;
    2702             :     }
    2703             :     else
    2704             :     {
    2705           1 :         basename = f_c_file.substr(pos + 1);
    2706             :     }
    2707             : 
    2708             :     out << "/* *** AUTO-GENERATED *** DO NOT EDIT ***\n"
    2709             :            " *\n"
    2710             :            " * This list of TLDs was auto-generated using the tldc compiler.\n"
    2711             :            " * Fix the tld_compiler.cpp or the .ini files used as input instead\n"
    2712             :            " * of this file.\n"
    2713             :            " *\n"
    2714             :            " * Copyright (c) 2011-" << year << "  Made to Order Software Corp.  All Rights Reserved.\n"
    2715             :            " *\n"
    2716             :            " * Permission is hereby granted, free of charge, to any person obtaining a\n"
    2717             :            " * copy of this software and associated documentation files (the\n"
    2718             :            " * \"Software\"), to deal in the Software without restriction, including\n"
    2719             :            " * without limitation the rights to use, copy, modify, merge, publish,\n"
    2720             :            " * distribute, sublicense, and/or sell copies of the Software, and to\n"
    2721             :            " * permit persons to whom the Software is furnished to do so, subject to\n"
    2722             :            " * the following conditions:\n"
    2723             :            " *\n"
    2724             :            " * The above copyright notice and this permission notice shall be included\n"
    2725             :            " * in all copies or substantial portions of the Software.\n"
    2726             :            " *\n"
    2727             :            " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n"
    2728             :            " * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"
    2729             :            " * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n"
    2730             :            " * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n"
    2731             :            " * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n"
    2732             :            " * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n"
    2733             :            " * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
    2734             :            " */\n"
    2735             :            "\n"
    2736             :            "/** \\file\n"
    2737           1 :            " * \\brief GENERATED FILE -- the " << basename << " file is generated -- DO NOT EDIT\n"
    2738             :            " *\n"
    2739             :            " * This file is generated using the tldc tool and the conf/tlds/... files.\n"
    2740             :            " * It is strongly advised that you do not edit this file directly except to\n"
    2741             :            " * test before editing the source of the tldc tool and tld_compiler.cpp file.\n"
    2742             :            " *\n"
    2743             :            " * The file includes information about all the TLDs as defined in the\n"
    2744             :            " * .ini files. It is used by the tld() function to determine whether\n"
    2745             :            " * a string with a domain name matches a valid TLD. It includes all the\n"
    2746             :            " * currently assigned TLDs (all countries plus international or common TLDs.)\n"
    2747             :            " *\n"
    2748             :            " * In this new implementation, the C version to compile is actually the\n"
    2749             :            " * RIFF/TLDS binary. We load it with the tld_file_load() function as if it\n"
    2750             :            " * were on disk. This way we have exactly the same code to load the\n"
    2751             :            " * compiled-in and the TLDs from files.\n"
    2752             :            " */\n"
    2753           1 :            "#include <stdint.h>\n";
    2754           1 : }
    2755             : 
    2756             : 
    2757           1 : void tld_compiler::save_to_c_file(std::string const & buffer)
    2758             : {
    2759             :     // user requested that file?
    2760             :     //
    2761           1 :     if(f_c_file.empty())
    2762             :     {
    2763           0 :         return;
    2764             :     }
    2765             : 
    2766           2 :     std::ofstream out;
    2767           1 :     out.open(f_c_file);
    2768           1 :     if(!out)
    2769             :     {
    2770           0 :         f_errno = errno;
    2771           0 :         f_errmsg = "error: could not open C-file output file \""
    2772           0 :                  + f_output
    2773           0 :                  + "\", errno: "
    2774           0 :                  + std::to_string(f_errno)
    2775           0 :                  + ", "
    2776           0 :                  + strerror(f_errno)
    2777           0 :                  + ".";
    2778           0 :         return;
    2779             :     }
    2780             : 
    2781           1 :     output_header(out);
    2782             : 
    2783           1 :     out << "uint8_t const tld_static_tlds[] = {\n"
    2784           1 :         << std::hex
    2785           1 :         << std::setfill('0');
    2786             : 
    2787       15776 :     for(std::uint32_t idx(0); idx + 16 < buffer.length(); idx += 16)
    2788             :     {
    2789       15775 :         out << "   ";
    2790      268175 :         for(std::uint32_t o(0); o < 16; ++o)
    2791             :         {
    2792             :             out << " 0x"
    2793      252400 :                 << std::setw(2)
    2794      252400 :                 << static_cast<int>(static_cast<uint8_t>(buffer[idx + o]))
    2795      252400 :                 << ",";
    2796             :         }
    2797       15775 :         out << "\n";
    2798             :     }
    2799           1 :     std::uint32_t const leftover(buffer.length() % 16);
    2800           1 :     std::uint32_t const offset(buffer.length() - leftover);
    2801           1 :     if(leftover > 0)
    2802             :     {
    2803           1 :         out << "   ";
    2804          11 :         for(std::uint32_t o(0); o < leftover; ++o)
    2805             :         {
    2806             :             out << " 0x"
    2807          10 :                 << std::setw(2)
    2808          10 :                 << static_cast<int>(static_cast<uint8_t>(buffer[offset + o]))
    2809          10 :                 << ",";
    2810             :         }
    2811           1 :         out << "\n";
    2812             :     }
    2813           1 :     out << "};\n";
    2814             : }
    2815             : 
    2816             : 
    2817           2 : void tld_compiler::output_to_json(std::ostream & out, bool verbose) const
    2818             : {
    2819           2 :     out << "{\n";
    2820           2 :     out << "\"version\":\"" << TLD_FILE_VERSION_MAJOR
    2821           2 :                      << '.' << TLD_FILE_VERSION_MINOR << "\",\n";
    2822           2 :     out << "\"created-on\":" << f_created_on << ",\n";
    2823           2 :     out << "\"max-level\":" << static_cast<int>(f_tld_max_level) << ",\n";
    2824           2 :     out << "\"tld-start-offset\":" << f_tld_start_offset << ",\n";
    2825           2 :     out << "\"tld-end-offset\":" << f_tld_end_offset << ",\n";
    2826           2 :     out << "\"descriptions\":[\n";
    2827       20930 :     for(std::size_t idx(0); idx < f_definitions.size(); ++idx)
    2828             :     {
    2829       20928 :         auto it(std::find_if(
    2830             :               f_definitions.begin()
    2831             :             , f_definitions.end()
    2832   109505760 :             , [idx](auto const & d)
    2833   109505760 :                 {
    2834   219011520 :                     return d.second->get_index() == static_cast<int>(idx);
    2835   109526688 :                 }));
    2836       20928 :         if(it == f_definitions.end())
    2837             :         {
    2838           0 :             std::cerr << "error: could not find definition at index "
    2839           0 :                 << idx
    2840           0 :                 << "\n";
    2841           0 :             return;
    2842             :         }
    2843             :         //out << "\"index\":\"" << it->second->get_index() << "\"";
    2844             : 
    2845       20928 :         out << (idx == 0 ? "" : ",\n");
    2846             : 
    2847       20928 :         out << "{";
    2848             : 
    2849       20928 :         if(verbose)
    2850             :         {
    2851       10464 :             out << "\"index\":" << std::setw(5) << idx << ",";
    2852             :         }
    2853             : 
    2854       20928 :         out << "\"tld\":\"" << f_strings.get_string(it->second->get_segments()[0]) << "\"";
    2855             : 
    2856       20928 :         out << ",\"status\":\"" << tld_status_to_string(it->second->get_status()) << "\"";
    2857             : 
    2858       20928 :         if(!it->second->get_apply_to().empty())
    2859             :         {
    2860          42 :             out << ",\"apply-to\":\"" << it->second->get_apply_to() << "\"";
    2861             :         }
    2862             : 
    2863       20928 :         if(it->second->get_start_offset() != USHRT_MAX)
    2864             :         {
    2865        1590 :             out << ",\"start-offset\":" << it->second->get_start_offset();
    2866        1590 :             out << ",\"end-offset\":" << it->second->get_end_offset();
    2867             :         }
    2868             : 
    2869       59822 :         for(auto const & t : it->second->get_tags())
    2870             :         {
    2871       77788 :             out << ",\"" << f_strings.get_string(t.first)
    2872       77788 :                 << "\":\"" << f_strings.get_string(t.second)
    2873      116682 :                 << "\"";
    2874             :         }
    2875             : 
    2876       20928 :         if(verbose)
    2877             :         {
    2878       10464 :             out << ",\"full-tld\":\"" << it->second->get_name() << "\"";
    2879             :         }
    2880             : 
    2881       20928 :         out << "}";
    2882             :     }
    2883           2 :     out << "]}\n";
    2884         729 : }
    2885             : 
    2886             : 
    2887             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13