LCOV - code coverage report
Current view: top level - tests - catch_config_file.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.5 % 2189 2177
Test Date: 2026-01-18 09:38:57 Functions: 100.0 % 16 16
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2006-2025  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/advgetopt
       4              : // contact@m2osw.com
       5              : //
       6              : // This program is free software; you can redistribute it and/or modify
       7              : // it under the terms of the GNU General Public License as published by
       8              : // the Free Software Foundation; either version 2 of the License, or
       9              : // (at your option) any later version.
      10              : //
      11              : // This program is distributed in the hope that it will be useful,
      12              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14              : // GNU General Public License for more details.
      15              : //
      16              : // You should have received a copy of the GNU General Public License along
      17              : // with this program; if not, write to the Free Software Foundation, Inc.,
      18              : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19              : 
      20              : // advgetopt
      21              : //
      22              : #include    <advgetopt/conf_file.h>
      23              : 
      24              : #include    <advgetopt/exception.h>
      25              : 
      26              : 
      27              : // self
      28              : //
      29              : #include    "catch_main.h"
      30              : 
      31              : 
      32              : // libutf8
      33              : //
      34              : #include    <libutf8/libutf8.h>
      35              : 
      36              : 
      37              : // snapdev
      38              : //
      39              : #include    <snapdev/safe_setenv.h>
      40              : #include    <snapdev/tokenize_string.h>
      41              : 
      42              : 
      43              : // C++
      44              : //
      45              : #include    <fstream>
      46              : #include    <iomanip>
      47              : 
      48              : 
      49              : // C
      50              : //
      51              : #include    <unistd.h>
      52              : 
      53              : 
      54              : // last include
      55              : //
      56              : #include    <snapdev/poison.h>
      57              : 
      58              : 
      59              : 
      60            1 : CATCH_TEST_CASE("configuration_spaces", "[config][getopt][valid]")
      61              : {
      62            1 :     CATCH_START_SECTION("configuration_spaces: verify configuration spaces")
      63              :     {
      64      1114113 :         for(int c(0); c < 0x110000; ++c)
      65              :         {
      66      1114112 :             if(c == '\r'
      67      1114111 :             || c == '\n')
      68              :             {
      69            2 :                 CATCH_REQUIRE_FALSE(advgetopt::iswspace(c));
      70            2 :             }
      71      1114110 :             else if(std::iswspace(c))
      72              :             {
      73            4 :                 CATCH_REQUIRE(advgetopt::iswspace(c));
      74              :             }
      75              :             else
      76              :             {
      77      1114106 :                 CATCH_REQUIRE_FALSE(advgetopt::iswspace(c));
      78              :             }
      79              :         }
      80              :     }
      81            1 :     CATCH_END_SECTION()
      82            1 : }
      83              : 
      84              : 
      85            2 : CATCH_TEST_CASE("configuration_setup", "[config][getopt][valid]")
      86              : {
      87            2 :     CATCH_START_SECTION("configuration_setup: check all setups")
      88              :     {
      89              :         // 5 * 6 * 16 * 8 * 16 = 61440
      90            6 :         for(int count(0); count < 5; ++count)
      91              :         {
      92            5 :             int const id(rand());
      93            5 :             std::string const name("setup-file-" + std::to_string(id));
      94              : 
      95           15 :             SNAP_CATCH2_NAMESPACE::init_tmp_dir("setup", name);
      96              : 
      97              :             {
      98            5 :                 std::ofstream config_file;
      99            5 :                 config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     100            5 :                 CATCH_REQUIRE(config_file.good());
     101              :                 config_file <<
     102              :                     "# Auto-generated\n"
     103            5 :                     "param=optional\n"
     104              :                 ;
     105            5 :             }
     106              : 
     107           35 :             for(int lc(static_cast<int>(advgetopt::line_continuation_t::line_continuation_single_line));
     108           35 :                 lc <= static_cast<int>(advgetopt::line_continuation_t::line_continuation_semicolon);
     109              :                 ++lc)
     110              :             {
     111          510 :                 for(advgetopt::assignment_operator_t ao(0);
     112          510 :                     ao <= advgetopt::ASSIGNMENT_OPERATOR_MASK;
     113              :                     ++ao)
     114              :                 {
     115         3840 :                     for(advgetopt::comment_t c(0);
     116         3840 :                         c < advgetopt::COMMENT_MASK;
     117              :                         ++c)
     118              :                     {
     119        53760 :                         for(advgetopt::section_operator_t so(0);
     120        53760 :                             so < advgetopt::SECTION_OPERATOR_MASK;
     121              :                             ++so)
     122              :                         {
     123        50400 :                             advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     124              :                                                 , static_cast<advgetopt::line_continuation_t>(lc)
     125              :                                                 , ao
     126              :                                                 , c
     127        50400 :                                                 , so);
     128              : 
     129        50400 :                             advgetopt::assignment_operator_t real_ao(ao == 0 ? advgetopt::ASSIGNMENT_OPERATOR_EQUAL : ao);
     130              : 
     131        50400 :                             CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     132              : 
     133        50400 :                             CATCH_REQUIRE(setup.is_valid());
     134        50400 :                             std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
     135        50400 :                             CATCH_REQUIRE(setup.get_filename() == fn.get());
     136        50400 :                             CATCH_REQUIRE(setup.get_line_continuation() == static_cast<advgetopt::line_continuation_t>(lc));
     137        50400 :                             CATCH_REQUIRE(setup.get_assignment_operator() == real_ao);
     138        50400 :                             CATCH_REQUIRE(setup.get_comment() == c);
     139        50400 :                             CATCH_REQUIRE(setup.get_section_operator() == so);
     140              : 
     141        50400 :                             std::string const url(setup.get_config_url());
     142              : //std::cerr << "+++ " << lc << " / " << ao << " / " << c << " / " << so << " URL [" << url << "]\n";
     143        50400 :                             CATCH_REQUIRE(url.substr(0, 8) == "file:///");
     144              : 
     145        50400 :                             CATCH_REQUIRE(url.substr(7, strlen(fn.get())) == fn.get());
     146              : 
     147        50400 :                             std::string::size_type const qm_pos(url.find('?'));
     148        50400 :                             if(qm_pos == std::string::npos)
     149              :                             {
     150              :                                 // must have the defaults in this case
     151              :                                 //
     152            0 :                                 CATCH_REQUIRE(static_cast<advgetopt::line_continuation_t>(lc) == advgetopt::line_continuation_t::line_continuation_unix);
     153            0 :                                 CATCH_REQUIRE(real_ao == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     154            0 :                                 CATCH_REQUIRE(c  == (advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL));
     155            0 :                                 CATCH_REQUIRE(so == advgetopt::SECTION_OPERATOR_INI_FILE);
     156              :                             }
     157              :                             else
     158              :                             {
     159        50400 :                                 std::string const qs(url.substr(qm_pos + 1));
     160              : 
     161        50400 :                                 std::vector<std::string> strings;
     162       151200 :                                 snapdev::tokenize_string(strings, qs, "&");
     163              : 
     164        50400 :                                 bool def_lc(true);
     165        50400 :                                 bool def_ao(true);
     166        50400 :                                 bool def_c(true);
     167        50400 :                                 bool def_so(true);
     168              : 
     169       230580 :                                 for(auto s : strings)
     170              :                                 {
     171       180180 :                                     std::string::size_type const eq_pos(s.find('='));
     172       180180 :                                     CATCH_REQUIRE(eq_pos != std::string::npos);
     173              : 
     174       180180 :                                     std::string const var_name(s.substr(0, eq_pos));
     175       180180 :                                     std::string const var_value(s.substr(eq_pos + 1));
     176              : 
     177       180180 :                                     if(var_name == "line-continuation")
     178              :                                     {
     179        42000 :                                         def_lc = false;
     180        42000 :                                         switch(static_cast<advgetopt::line_continuation_t>(lc))
     181              :                                         {
     182         8400 :                                         case advgetopt::line_continuation_t::line_continuation_single_line:
     183         8400 :                                             CATCH_REQUIRE(var_value == "single-line");
     184         8400 :                                             break;
     185              : 
     186         8400 :                                         case advgetopt::line_continuation_t::line_continuation_rfc_822:
     187         8400 :                                             CATCH_REQUIRE(var_value == "rfc-822");
     188         8400 :                                             break;
     189              : 
     190         8400 :                                         case advgetopt::line_continuation_t::line_continuation_msdos:
     191         8400 :                                             CATCH_REQUIRE(var_value == "msdos");
     192         8400 :                                             break;
     193              : 
     194            0 :                                         case advgetopt::line_continuation_t::line_continuation_unix:
     195            0 :                                             CATCH_REQUIRE(var_value == "unix");
     196            0 :                                             break;
     197              : 
     198         8400 :                                         case advgetopt::line_continuation_t::line_continuation_fortran:
     199         8400 :                                             CATCH_REQUIRE(var_value == "fortran");
     200         8400 :                                             break;
     201              : 
     202         8400 :                                         case advgetopt::line_continuation_t::line_continuation_semicolon:
     203         8400 :                                             CATCH_REQUIRE(var_value == "semi-colon");
     204         8400 :                                             break;
     205              : 
     206            0 :                                         default:
     207            0 :                                             CATCH_REQUIRE(("unknown_var_value for \"line-continuation\":" + var_value) == std::string());
     208            0 :                                             break;
     209              : 
     210              :                                         }
     211              :                                     }
     212       138180 :                                     else if(var_name == "assignment-operator")
     213              :                                     {
     214        44100 :                                         def_ao = false;
     215        44100 :                                         std::vector<std::string> operators;
     216       132300 :                                         snapdev::tokenize_string(operators, var_value, ",");
     217              : 
     218        44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_EQUAL) != 0)
     219              :                                         {
     220        22050 :                                             auto it(std::find(operators.begin(), operators.end(), "equal"));
     221        22050 :                                             CATCH_REQUIRE(it != operators.end());
     222        22050 :                                             operators.erase(it);
     223              :                                         }
     224        44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_COLON) != 0)
     225              :                                         {
     226        25200 :                                             auto it(std::find(operators.begin(), operators.end(), "colon"));
     227        25200 :                                             CATCH_REQUIRE(it != operators.end());
     228        25200 :                                             operators.erase(it);
     229              :                                         }
     230        44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_SPACE) != 0)
     231              :                                         {
     232        25200 :                                             auto it(std::find(operators.begin(), operators.end(), "space"));
     233        25200 :                                             CATCH_REQUIRE(it != operators.end());
     234        25200 :                                             operators.erase(it);
     235              :                                         }
     236        44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_EXTENDED) != 0)
     237              :                                         {
     238        25200 :                                             auto it(std::find(operators.begin(), operators.end(), "extended"));
     239        25200 :                                             CATCH_REQUIRE(it != operators.end());
     240        25200 :                                             operators.erase(it);
     241              :                                         }
     242              : 
     243        44100 :                                         CATCH_REQUIRE(operators.empty());
     244        44100 :                                     }
     245        94080 :                                     else if(var_name == "comment")
     246              :                                     {
     247        50400 :                                         def_c = false;
     248        50400 :                                         std::vector<std::string> comments;
     249       151200 :                                         snapdev::tokenize_string(comments, var_value, ",");
     250              : 
     251        50400 :                                         if((c & advgetopt::COMMENT_INI) != 0)
     252              :                                         {
     253        21600 :                                             auto it(std::find(comments.begin(), comments.end(), "ini"));
     254        21600 :                                             CATCH_REQUIRE(it != comments.end());
     255        21600 :                                             comments.erase(it);
     256              :                                         }
     257        50400 :                                         if((c & advgetopt::COMMENT_SHELL) != 0)
     258              :                                         {
     259        21600 :                                             auto it(std::find(comments.begin(), comments.end(), "shell"));
     260        21600 :                                             CATCH_REQUIRE(it != comments.end());
     261        21600 :                                             comments.erase(it);
     262              :                                         }
     263        50400 :                                         if((c & advgetopt::COMMENT_CPP) != 0)
     264              :                                         {
     265        21600 :                                             auto it(std::find(comments.begin(), comments.end(), "cpp"));
     266        21600 :                                             CATCH_REQUIRE(it != comments.end());
     267        21600 :                                             comments.erase(it);
     268              :                                         }
     269        50400 :                                         if(c == advgetopt::COMMENT_NONE)
     270              :                                         {
     271         7200 :                                             auto it(std::find(comments.begin(), comments.end(), "none"));
     272         7200 :                                             CATCH_REQUIRE(it != comments.end());
     273         7200 :                                             comments.erase(it);
     274              :                                         }
     275              : 
     276        50400 :                                         CATCH_REQUIRE(comments.empty());
     277        50400 :                                     }
     278        43680 :                                     else if(var_name == "section-operator")
     279              :                                     {
     280        43680 :                                         def_so = false;
     281        43680 :                                         std::vector<std::string> section_operators;
     282       131040 :                                         snapdev::tokenize_string(section_operators, var_value, ",");
     283              : 
     284        43680 :                                         if((so & advgetopt::SECTION_OPERATOR_C) != 0)
     285              :                                         {
     286        23520 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "c"));
     287        23520 :                                             CATCH_REQUIRE(it != section_operators.end());
     288        23520 :                                             section_operators.erase(it);
     289              :                                         }
     290        43680 :                                         if((so & advgetopt::SECTION_OPERATOR_CPP) != 0)
     291              :                                         {
     292        23520 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "cpp"));
     293        23520 :                                             CATCH_REQUIRE(it != section_operators.end());
     294        23520 :                                             section_operators.erase(it);
     295              :                                         }
     296        43680 :                                         if((so & advgetopt::SECTION_OPERATOR_BLOCK) != 0)
     297              :                                         {
     298        23520 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "block"));
     299        23520 :                                             CATCH_REQUIRE(it != section_operators.end());
     300        23520 :                                             section_operators.erase(it);
     301              :                                         }
     302        43680 :                                         if((so & advgetopt::SECTION_OPERATOR_INI_FILE) != 0)
     303              :                                         {
     304        20160 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "ini-file"));
     305        20160 :                                             CATCH_REQUIRE(it != section_operators.end());
     306        20160 :                                             section_operators.erase(it);
     307              :                                         }
     308              : 
     309        43680 :                                         CATCH_REQUIRE(section_operators.empty());
     310        43680 :                                     }
     311              :                                     else
     312              :                                     {
     313            0 :                                         CATCH_REQUIRE(("unknown var_name = " + var_name) == std::string());
     314              :                                     }
     315       180180 :                                 }
     316              : 
     317        50400 :                                 if(def_lc)
     318              :                                 {
     319         8400 :                                     CATCH_REQUIRE(static_cast<advgetopt::line_continuation_t>(lc) == advgetopt::line_continuation_t::line_continuation_unix);
     320              :                                 }
     321        50400 :                                 if(def_ao)
     322              :                                 {
     323         6300 :                                     CATCH_REQUIRE(real_ao == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     324              :                                 }
     325        50400 :                                 if(def_c)
     326              :                                 {
     327            0 :                                     CATCH_REQUIRE(c == (advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL));
     328              :                                 }
     329        50400 :                             }
     330        50400 :                         }
     331              :                     }
     332              :                 }
     333              :             }
     334            5 :         }
     335              :     }
     336            2 :     CATCH_END_SECTION()
     337              : 
     338            2 :     CATCH_START_SECTION("configuration_setup: check non-existent filename")
     339              :     {
     340            1 :         advgetopt::conf_file_setup setup(
     341              :                       "/etc/advgetopt/unknown-file.conf"
     342              :                     , advgetopt::line_continuation_t::line_continuation_fortran
     343              :                     , advgetopt::ASSIGNMENT_OPERATOR_COLON
     344              :                     , advgetopt::COMMENT_INI
     345            3 :                     , advgetopt::SECTION_OPERATOR_CPP);
     346              : 
     347            1 :         CATCH_REQUIRE(setup.get_original_filename() == "/etc/advgetopt/unknown-file.conf");
     348              : 
     349            1 :         CATCH_REQUIRE(setup.is_valid());
     350            1 :         CATCH_REQUIRE(setup.get_filename() == "/etc/advgetopt/unknown-file.conf");
     351            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_fortran);
     352            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
     353            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_INI);
     354            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_CPP);
     355              : 
     356            1 :         CATCH_REQUIRE(setup.get_config_url() == "file:///etc/advgetopt/unknown-file.conf?line-continuation=fortran&assignment-operator=colon&comment=ini&section-operator=cpp");
     357            1 :     }
     358            2 :     CATCH_END_SECTION()
     359            2 : }
     360              : 
     361              : 
     362              : 
     363            1 : CATCH_TEST_CASE("config_reload_tests", "[config][getopt][valid]")
     364              : {
     365            1 :     CATCH_START_SECTION("config_reload_tests: load a file, update it, verify it does not get reloaded")
     366              :     {
     367            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("reload", "load-twice");
     368              : 
     369              :         {
     370            1 :             std::ofstream config_file;
     371            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     372            1 :             CATCH_REQUIRE(config_file.good());
     373              :             config_file <<
     374              :                 "# Auto-generated\n"
     375              :                 "param=value\n"
     376              :                 "changing=without reloading is useless\n"
     377            1 :                 "test=1009\n"
     378              :             ;
     379            1 :         }
     380              : 
     381            1 :         advgetopt::conf_file::pointer_t file1;
     382              :         {
     383            1 :             advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     384              :                                 , advgetopt::line_continuation_t::line_continuation_single_line
     385              :                                 , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     386              :                                 , advgetopt::COMMENT_SHELL
     387            1 :                                 , advgetopt::SECTION_OPERATOR_NONE);
     388              : 
     389            1 :             CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     390              : 
     391            1 :             CATCH_REQUIRE(setup.is_valid());
     392            1 :             CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     393            1 :             CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     394            1 :             CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     395            1 :             CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     396              : 
     397            1 :             file1 = advgetopt::conf_file::get_conf_file(setup);
     398              : 
     399            1 :             CATCH_REQUIRE(file1->get_setup().get_config_url() == setup.get_config_url());
     400            1 :             CATCH_REQUIRE(file1->get_errno() == 0);
     401            1 :             CATCH_REQUIRE(file1->get_sections().empty());
     402            1 :             CATCH_REQUIRE(file1->get_parameters().size() == 3);
     403              : 
     404            3 :             CATCH_REQUIRE(file1->has_parameter("param"));
     405            3 :             CATCH_REQUIRE(file1->has_parameter("changing"));
     406            3 :             CATCH_REQUIRE(file1->has_parameter("test"));
     407              : 
     408            3 :             CATCH_REQUIRE(file1->get_parameter("param") == "value");
     409            3 :             CATCH_REQUIRE(file1->get_parameter("changing") == "without reloading is useless");
     410            3 :             CATCH_REQUIRE(file1->get_parameter("test") == "1009");
     411            1 :         }
     412              : 
     413              :         // change all the values now
     414              :         {
     415            1 :             std::ofstream config_file;
     416            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     417            1 :             CATCH_REQUIRE(config_file.good());
     418              :             config_file <<
     419              :                 "# Auto-generated\n"
     420              :                 "param=new data\n"
     421              :                 "new=this is not even acknowledge\n"
     422              :                 "changing=special value\n"
     423              :                 "test=9010\n"
     424            1 :                 "level=three\n"
     425              :             ;
     426            1 :         }
     427              : 
     428              :         // "reloading" that very same file has the old data
     429              :         {
     430            1 :             advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     431              :                                 , advgetopt::line_continuation_t::line_continuation_single_line
     432              :                                 , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     433              :                                 , advgetopt::COMMENT_SHELL
     434            1 :                                 , advgetopt::SECTION_OPERATOR_NONE);
     435              : 
     436            1 :             CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     437              : 
     438            1 :             CATCH_REQUIRE(setup.is_valid());
     439            1 :             CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     440            1 :             CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     441            1 :             CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     442            1 :             CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     443              : 
     444            1 :             advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     445              : 
     446              :             // exact same pointer
     447              :             //
     448            1 :             CATCH_REQUIRE(file == file1);
     449              : 
     450            1 :             CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     451            1 :             CATCH_REQUIRE(file->get_errno() == 0);
     452            1 :             CATCH_REQUIRE(file->get_sections().empty());
     453            1 :             CATCH_REQUIRE(file->get_parameters().size() == 3);
     454              : 
     455            3 :             CATCH_REQUIRE(file->has_parameter("param"));
     456            3 :             CATCH_REQUIRE(file->has_parameter("changing"));
     457            3 :             CATCH_REQUIRE(file->has_parameter("test"));
     458              : 
     459            3 :             CATCH_REQUIRE(file->get_parameter("param") == "value");
     460            3 :             CATCH_REQUIRE(file->get_parameter("changing") == "without reloading is useless");
     461            3 :             CATCH_REQUIRE(file->get_parameter("test") == "1009");
     462            1 :         }
     463            1 :     }
     464            1 :     CATCH_END_SECTION()
     465            1 : }
     466              : 
     467              : 
     468              : 
     469            1 : CATCH_TEST_CASE("config_duplicated_variables", "[config][getopt][valid]")
     470              : {
     471            1 :     CATCH_START_SECTION("config_duplicated_variables: file with the same variable defined multiple times")
     472              :     {
     473            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("duplicated-variable", "multiple");
     474              : 
     475              :         {
     476            1 :             std::ofstream config_file;
     477            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     478            1 :             CATCH_REQUIRE(config_file.good());
     479              :             config_file <<
     480              :                 "# Auto-generated\n"
     481              :                 "unique    = perfect  \n"
     482              :                 "multiple  = defintions\n"
     483              :                 "another   = just fine \t\n"
     484              :                 "multiple  = value\r\n"
     485              :                 "good      = variable \n"
     486              :                 "multiple  = set\n"
     487            1 :                 "more      = data\t \n"
     488              :             ;
     489            1 :         }
     490              : 
     491            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     492              :                             , advgetopt::line_continuation_t::line_continuation_single_line
     493              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     494              :                             , advgetopt::COMMENT_SHELL
     495            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     496              : 
     497            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     498              : 
     499            1 :         CATCH_REQUIRE(setup.is_valid());
     500            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     501            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     502            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     503            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     504              : 
     505            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
     506            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     507              :                       "warning: parameter \"multiple\" on line 5 in"
     508              :                       " configuration file \""
     509            4 :                     + std::string(fn.get())
     510            4 :                     + "\" was found twice in the same configuration file.");
     511            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     512              :                       "warning: parameter \"multiple\" on line 7 in"
     513              :                       " configuration file \""
     514            4 :                     + std::string(fn.get())
     515            4 :                     + "\" was found twice in the same configuration file.");
     516            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     517            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
     518              : 
     519            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     520            1 :         CATCH_REQUIRE(file->get_errno() == 0);
     521            1 :         CATCH_REQUIRE(file->get_sections().empty());
     522            1 :         CATCH_REQUIRE(file->get_parameters().size() == 5);
     523              : 
     524            3 :         CATCH_REQUIRE(file->has_parameter("unique"));
     525            3 :         CATCH_REQUIRE(file->has_parameter("another"));
     526            3 :         CATCH_REQUIRE(file->has_parameter("good"));
     527            3 :         CATCH_REQUIRE(file->has_parameter("more"));
     528            3 :         CATCH_REQUIRE(file->has_parameter("multiple"));
     529              : 
     530            3 :         CATCH_REQUIRE(file->get_parameter("unique") == "perfect");
     531            3 :         CATCH_REQUIRE(file->get_parameter("another") == "just fine");
     532            3 :         CATCH_REQUIRE(file->get_parameter("good") == "variable");
     533            3 :         CATCH_REQUIRE(file->get_parameter("more") == "data");
     534            3 :         CATCH_REQUIRE(file->get_parameter("multiple") == "set");
     535              : 
     536              :         // we get a warning while reading; but not when directly
     537              :         // accessing the file object
     538              :         //
     539            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "multiple", "new value"));
     540            3 :         CATCH_REQUIRE(file->get_parameter("multiple") == "new value");
     541            1 :     }
     542            1 :     CATCH_END_SECTION()
     543            1 : }
     544              : 
     545              : 
     546              : 
     547            1 : CATCH_TEST_CASE("config_callback_calls", "[config][getopt][valid]")
     548              : {
     549            1 :     CATCH_START_SECTION("config_callback_calls: setup a callback and test the set_parameter()/erase() functions")
     550              :     {
     551            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("callback-variable", "callback");
     552              : 
     553              :         {
     554            1 :             std::ofstream config_file;
     555            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     556            1 :             CATCH_REQUIRE(config_file.good());
     557              :             config_file <<
     558              :                 "# Auto-generated\n"
     559              :                 "unique     = perfect  \n"
     560              :                 "definition = long value here\n"
     561              :                 "another    = just fine \t\n"
     562              :                 "multiple   = value\r\n"
     563              :                 "good       = variable \n"
     564              :                 "organized  = set\n"
     565            1 :                 "more       = data\t \n"
     566              :             ;
     567            1 :         }
     568              : 
     569            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     570              :                             , advgetopt::line_continuation_t::line_continuation_single_line
     571              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     572              :                             , advgetopt::COMMENT_SHELL
     573            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     574              : 
     575            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     576              : 
     577            1 :         CATCH_REQUIRE(setup.is_valid());
     578            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     579            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     580            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     581            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     582              : 
     583            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     584              : 
     585              :         struct conf_data
     586              :         {
     587              :             advgetopt::conf_file::pointer_t f_conf_file = advgetopt::conf_file::pointer_t();
     588              :             advgetopt::callback_action_t    f_expected_action = advgetopt::callback_action_t::created;
     589              :             std::string                     f_expected_variable = std::string();
     590              :             std::string                     f_expected_value = std::string();
     591              :         };
     592            1 :         conf_data cf_data;
     593            1 :         cf_data.f_conf_file = file;
     594              : 
     595              :         struct conf_callback
     596              :         {
     597            6 :             void operator () (advgetopt::conf_file::pointer_t conf_file
     598              :                             , advgetopt::callback_action_t action
     599              :                             , std::string const & variable_name
     600              :                             , std::string const & value)
     601              :             {
     602            6 :                 CATCH_REQUIRE(conf_file == f_data->f_conf_file);
     603            6 :                 CATCH_REQUIRE(action == f_data->f_expected_action);
     604            6 :                 CATCH_REQUIRE(variable_name == f_data->f_expected_variable);
     605            6 :                 CATCH_REQUIRE(value == f_data->f_expected_value);
     606            6 :                 CATCH_REQUIRE(conf_file->get_parameter(variable_name) == f_data->f_expected_value);
     607            6 :             }
     608              : 
     609              :             conf_data * f_data = nullptr;
     610              :         };
     611            1 :         conf_callback cf;
     612            1 :         cf.f_data = &cf_data;
     613              : 
     614            1 :         advgetopt::conf_file::callback_id_t callback_id(file->add_callback(cf));
     615              : 
     616            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     617            1 :         CATCH_REQUIRE(file->get_errno() == 0);
     618            1 :         CATCH_REQUIRE_FALSE(file->was_modified());
     619            1 :         CATCH_REQUIRE(file->get_sections().empty());
     620            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
     621              : 
     622            3 :         CATCH_REQUIRE(file->has_parameter("unique"));
     623            3 :         CATCH_REQUIRE(file->has_parameter("definition"));
     624            3 :         CATCH_REQUIRE(file->has_parameter("another"));
     625            3 :         CATCH_REQUIRE(file->has_parameter("multiple"));
     626            3 :         CATCH_REQUIRE(file->has_parameter("good"));
     627            3 :         CATCH_REQUIRE(file->has_parameter("organized"));
     628            3 :         CATCH_REQUIRE(file->has_parameter("more"));
     629              : 
     630            3 :         CATCH_REQUIRE(file->get_parameter("unique") == "perfect");
     631            3 :         CATCH_REQUIRE(file->get_parameter("definition") == "long value here");
     632            3 :         CATCH_REQUIRE(file->get_parameter("another") == "just fine");
     633            3 :         CATCH_REQUIRE(file->get_parameter("multiple") == "value");
     634            3 :         CATCH_REQUIRE(file->get_parameter("good") == "variable");
     635            3 :         CATCH_REQUIRE(file->get_parameter("organized") == "set");
     636            3 :         CATCH_REQUIRE(file->get_parameter("more") == "data");
     637              : 
     638              :         // updated action
     639              :         //
     640            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::updated;
     641            1 :         cf_data.f_expected_variable = "multiple";
     642            1 :         cf_data.f_expected_value = "new value";
     643            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "multiple", "new value"));
     644            1 :         CATCH_REQUIRE(file->was_modified());
     645            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
     646            3 :         CATCH_REQUIRE(file->get_parameter("multiple") == "new value");
     647              : 
     648              :         // created action
     649              :         //
     650            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::created;
     651            1 :         cf_data.f_expected_variable = "new-param";
     652            1 :         cf_data.f_expected_value = "with this value";
     653            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "with this value"));
     654            1 :         CATCH_REQUIRE(file->was_modified());
     655            1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     656            3 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     657            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == "with this value");
     658            3 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     659            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == "with this value");
     660              : 
     661              :         // updated action when modifying
     662              :         //
     663            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::updated;
     664            1 :         cf_data.f_expected_variable = "new-param";
     665            1 :         cf_data.f_expected_value = "change completely";
     666            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "change completely"));
     667            1 :         CATCH_REQUIRE(file->was_modified());
     668            1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     669            3 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     670            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == "change completely");
     671            3 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     672            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == "change completely");
     673              : 
     674              :         // erased action
     675              :         //
     676            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::erased;
     677            1 :         cf_data.f_expected_variable = "new-param";
     678            1 :         cf_data.f_expected_value = std::string();
     679            3 :         CATCH_REQUIRE(file->erase_parameter("new_param"));
     680            1 :         CATCH_REQUIRE(file->was_modified());
     681            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
     682            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("new-param"));
     683            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == std::string());
     684            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("new_param"));
     685            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == std::string());
     686            3 :         CATCH_REQUIRE_FALSE(file->erase_parameter("new_param"));
     687              : 
     688              :         // created action again (because it was erased)
     689              :         //
     690            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::created;
     691            1 :         cf_data.f_expected_variable = "new-param";
     692            1 :         cf_data.f_expected_value = "with this value";
     693            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "with this value"));
     694            1 :         CATCH_REQUIRE(file->was_modified());
     695            1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     696            3 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     697            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == "with this value");
     698            3 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     699            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == "with this value");
     700              : 
     701            1 :         file->remove_callback(callback_id);
     702            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::created;
     703            1 :         cf_data.f_expected_variable = "ignored";
     704            1 :         cf_data.f_expected_value = "ignored";
     705            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "unnoticed change"));
     706            1 :         CATCH_REQUIRE(file->was_modified());
     707            1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     708            3 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     709            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == "unnoticed change");
     710            3 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     711            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == "unnoticed change");
     712              : 
     713              :         // further calls do nothing more
     714              :         //
     715            1 :         file->remove_callback(callback_id);
     716            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "still unnoticed"));
     717            1 :         CATCH_REQUIRE(file->was_modified());
     718            1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     719            3 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     720            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == "still unnoticed");
     721            3 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     722            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == "still unnoticed");
     723              : 
     724              :         // and we can always re-add it
     725              :         //
     726            1 :         CATCH_REQUIRE(callback_id != file->add_callback(cf));
     727            1 :         cf_data.f_expected_action = advgetopt::callback_action_t::updated;
     728            1 :         cf_data.f_expected_variable = "new-param";
     729            1 :         cf_data.f_expected_value = "we're back";
     730            5 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "we're back"));
     731            1 :         CATCH_REQUIRE(file->was_modified());
     732            1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     733            3 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     734            3 :         CATCH_REQUIRE(file->get_parameter("new-param") == "we're back");
     735            3 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     736            3 :         CATCH_REQUIRE(file->get_parameter("new_param") == "we're back");
     737              : 
     738              :         // until you save it remains true even if you were to restore the
     739              :         // state to "normal" (we do not keep a copy of the original value
     740              :         // as found in the file.)
     741              :         //
     742            1 :         CATCH_REQUIRE(file->was_modified());
     743            1 :     }
     744            1 :     CATCH_END_SECTION()
     745            1 : }
     746              : 
     747              : 
     748              : 
     749            7 : CATCH_TEST_CASE("config_line_continuation_tests", "[config][getopt][valid]")
     750              : {
     751            7 :     CATCH_START_SECTION("config_line_continuation_tests: single_line (EQUAL)")
     752              :     {
     753            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "single-line");
     754              : 
     755              :         {
     756            1 :             std::ofstream config_file;
     757            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     758            1 :             CATCH_REQUIRE(config_file.good());
     759              :             config_file <<
     760              :                 "# Auto-generated\n"
     761              :                 "normal=param\n"
     762              :                 "\n"
     763              :                 "rfc-822=start here\n"
     764              :                 "  continue=there\n"
     765              :                 "\n"
     766              :                 "msdos=end with &\n"
     767              :                 "  and-continue=on next line\n"
     768              :                 "\n"
     769              :                 "unix=end with \\\n"
     770              :                 "to-continue=like this\n"
     771              :                 "\n"
     772              :                 "fortran=fortran is funny\n"
     773              :                 "&since=it starts with an & on the following line\n"
     774              :                 "\n"
     775              :                 "semicolon=this ends with\n"
     776            1 :                 "a=semi-colon only;\n"
     777              :             ;
     778            1 :         }
     779              : 
     780            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     781              :                             , advgetopt::line_continuation_t::line_continuation_single_line
     782              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     783              :                             , advgetopt::COMMENT_SHELL
     784            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     785              : 
     786            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     787              : 
     788            1 :         CATCH_REQUIRE(setup.is_valid());
     789            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     790            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     791            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     792            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     793              : 
     794            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     795              : 
     796            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     797            1 :         CATCH_REQUIRE(file->get_errno() == 0);
     798            1 :         CATCH_REQUIRE(file->get_sections().empty());
     799            1 :         CATCH_REQUIRE(file->get_parameters().size() == 11);
     800              : 
     801            3 :         CATCH_REQUIRE(file->has_parameter("normal"));
     802            3 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
     803            3 :         CATCH_REQUIRE(file->has_parameter("continue"));
     804            3 :         CATCH_REQUIRE(file->has_parameter("msdos"));
     805            3 :         CATCH_REQUIRE(file->has_parameter("and-continue"));
     806            3 :         CATCH_REQUIRE(file->has_parameter("unix"));
     807            3 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
     808            3 :         CATCH_REQUIRE(file->has_parameter("fortran"));
     809            3 :         CATCH_REQUIRE(file->has_parameter("&since"));
     810            3 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
     811            3 :         CATCH_REQUIRE(file->has_parameter("a"));
     812              : 
     813            3 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
     814            3 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
     815            3 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
     816            3 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &");
     817            3 :         CATCH_REQUIRE(file->get_parameter("and-continue") == "on next line");
     818            3 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
     819            3 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
     820            3 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
     821            3 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
     822            3 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
     823            3 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
     824              : 
     825      1114113 :         for(int c(0); c < 0x110000; ++c)
     826              :         {
     827      1114112 :             if(c >= 0xD800 && c <= 0xDFFFF)
     828              :             {
     829       862208 :                 continue;
     830              :             }
     831       251904 :             std::string const u(libutf8::to_u8string(static_cast<char32_t>(c)));
     832       251904 :             char const * s(u.c_str());
     833       251904 :             if(c == '=')
     834              :             {
     835            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_SET);
     836            1 :                 CATCH_REQUIRE(s == u.c_str());
     837            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_SET);
     838            1 :                 CATCH_REQUIRE(s == u.c_str() + 1);
     839              :             }
     840              :             else
     841              :             {
     842       251903 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     843       251903 :                 CATCH_REQUIRE(s == u.c_str());
     844       251903 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     845       251903 :                 CATCH_REQUIRE(s == u.c_str());
     846              :             }
     847       251904 :         }
     848            1 :     }
     849            7 :     CATCH_END_SECTION()
     850              : 
     851            7 :     CATCH_START_SECTION("config_line_continuation_tests: single_line (EXTENDED EQUALS)")
     852              :     {
     853            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("extended-equals", "equal-extensions");
     854              : 
     855              :         {
     856            1 :             std::ofstream config_file;
     857            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     858            1 :             CATCH_REQUIRE(config_file.good());
     859              :             config_file <<
     860              :                 "# Auto-generated\n"
     861              :                 "equal=set\n"
     862              :                 "equal=final\n"
     863              :                 "\n"
     864              :                 "optional?=keep-first\n"
     865              :                 "optional?=ignore-second\n"
     866              :                 "\n"
     867              :                 "append=first few words\n"
     868              :                 "append+=\" and a few last words\"\n"
     869              :                 "\n"
     870              :                 "new=only\n"
     871            1 :                 "new:=boom\n"
     872              :             ;
     873            1 :         }
     874              : 
     875            1 :         advgetopt::assignment_operator_t const assignment(
     876              :                   advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     877              :                 | advgetopt::ASSIGNMENT_OPERATOR_EXTENDED);
     878            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     879              :                             , advgetopt::line_continuation_t::line_continuation_single_line
     880              :                             , assignment
     881              :                             , advgetopt::COMMENT_SHELL
     882            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     883              : 
     884            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     885              : 
     886            1 :         CATCH_REQUIRE(setup.is_valid());
     887            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     888            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == assignment);
     889            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     890            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     891              : 
     892              :         // the simplest to test the ?=, +=, and := is to capture the errors
     893              :         // here so we do that...
     894              :         //
     895            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     896              :                   "warning: parameter \"equal\" on line 3 in configuration file \""
     897            2 :                 + setup.get_filename()
     898            4 :                 + "\" was found twice in the same configuration file.");
     899            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     900              :                   "warning: parameter \"optional\" on line 6 in configuration file \""
     901            2 :                 + setup.get_filename()
     902            4 :                 + "\" was found twice in the same configuration file.");
     903            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     904              :                   "warning: parameter \"append\" on line 9 in configuration file \""
     905            2 :                 + setup.get_filename()
     906            4 :                 + "\" was found twice in the same configuration file.");
     907            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     908              :                   "warning: parameter \"new\" on line 12 in configuration file \""
     909            2 :                 + setup.get_filename()
     910            4 :                 + "\" was found twice in the same configuration file.");
     911            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     912              :                   "error: parameter \"new\" is already defined and it cannot be overridden"
     913              :                   " with the ':=' operator on line 12 from configuration file \""
     914            2 :                 + setup.get_filename()
     915            4 :                 + "\".");
     916            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     917            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
     918              : 
     919            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     920            1 :         CATCH_REQUIRE(file->get_errno() == 0);
     921            1 :         CATCH_REQUIRE(file->get_sections().empty());
     922            1 :         CATCH_REQUIRE(file->get_parameters().size() == 4);
     923              : 
     924            3 :         CATCH_REQUIRE(file->has_parameter("equal"));
     925            3 :         CATCH_REQUIRE(file->has_parameter("optional"));
     926            3 :         CATCH_REQUIRE(file->has_parameter("append"));
     927            3 :         CATCH_REQUIRE(file->has_parameter("new"));
     928              : 
     929            3 :         CATCH_REQUIRE(file->get_parameter("equal") == "final");
     930            3 :         CATCH_REQUIRE(file->get_parameter("optional") == "keep-first");
     931            3 :         CATCH_REQUIRE(file->get_parameter("append") == "first few words and a few last words");
     932            3 :         CATCH_REQUIRE(file->get_parameter("new") == "only");
     933              : 
     934            1 :         char const * s = nullptr;
     935      1114113 :         for(int c(0); c < 0x110000; ++c)
     936              :         {
     937      1114112 :             if(c >= 0xD800 && c <= 0xDFFFF)
     938              :             {
     939       862208 :                 continue;
     940              :             }
     941       251904 :             std::string u(libutf8::to_u8string(static_cast<char32_t>(c)));
     942       251904 :             switch(c)
     943              :             {
     944            1 :             case '=':
     945            1 :                 s = u.c_str();
     946            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_SET);
     947            1 :                 CATCH_REQUIRE(s == u.c_str());
     948            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_SET);
     949            1 :                 CATCH_REQUIRE(s == u.c_str() + 1);
     950            1 :                 break;
     951              : 
     952            1 :             case '+':
     953            1 :                 s = u.c_str();
     954            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     955            1 :                 CATCH_REQUIRE(s == u.c_str());
     956            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     957            1 :                 CATCH_REQUIRE(s == u.c_str());
     958            1 :                 u += '=';
     959            1 :                 s = u.c_str();
     960            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_APPEND);
     961            1 :                 CATCH_REQUIRE(s == u.c_str());
     962            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_APPEND);
     963            1 :                 CATCH_REQUIRE(s == u.c_str() + 2);
     964            1 :                 break;
     965              : 
     966            1 :             case '?':
     967            1 :                 s = u.c_str();
     968            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     969            1 :                 CATCH_REQUIRE(s == u.c_str());
     970            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     971            1 :                 CATCH_REQUIRE(s == u.c_str());
     972            1 :                 u += '=';
     973            1 :                 s = u.c_str();
     974            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_OPTIONAL);
     975            1 :                 CATCH_REQUIRE(s == u.c_str());
     976            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_OPTIONAL);
     977            1 :                 CATCH_REQUIRE(s == u.c_str() + 2);
     978            1 :                 break;
     979              : 
     980            1 :             case ':':
     981              :                 // note that ':' is a valid assignment, but in this case we
     982              :                 // did not authorize it so it will return NONE
     983              :                 //
     984            1 :                 s = u.c_str();
     985            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     986            1 :                 CATCH_REQUIRE(s == u.c_str());
     987            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     988            1 :                 CATCH_REQUIRE(s == u.c_str());
     989            1 :                 u += '=';
     990            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NEW);
     991            1 :                 CATCH_REQUIRE(s == u.c_str());
     992            1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NEW);
     993            1 :                 CATCH_REQUIRE(s == u.c_str() + 2);
     994            1 :                 break;
     995              : 
     996       251900 :             default:
     997       251900 :                 s = u.c_str();
     998       251900 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     999       251900 :                 CATCH_REQUIRE(s == u.c_str());
    1000       251900 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
    1001       251900 :                 CATCH_REQUIRE(s == u.c_str());
    1002       251900 :                 break;
    1003              : 
    1004              :             }
    1005       251904 :         }
    1006            1 :     }
    1007            7 :     CATCH_END_SECTION()
    1008              : 
    1009            7 :     CATCH_START_SECTION("config_line_continuation_tests: rfc822")
    1010              :     {
    1011            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "rfc822");
    1012              : 
    1013              :         {
    1014            1 :             std::ofstream config_file;
    1015            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1016            1 :             CATCH_REQUIRE(config_file.good());
    1017              :             config_file <<
    1018              :                 "# Auto-generated\n"
    1019              :                 "normal=param\n"
    1020              :                 "\n"
    1021              :                 "rfc-822=start here\n"
    1022              :                 "  continue=there\n"
    1023              :                 "\n"
    1024              :                 "msdos=end with &\n"
    1025              :                 "  and-continue=on next line\n"
    1026              :                 "\n"
    1027              :                 "unix=end with \\\n"
    1028              :                 "to-continue=like this\n"
    1029              :                 "\n"
    1030              :                 "fortran=fortran is funny\n"
    1031              :                 "&since=it starts with an & on the following line\n"
    1032              :                 "\n"
    1033              :                 "semicolon=this ends with\n"
    1034            1 :                 "a=semi-colon only;\n"
    1035              :             ;
    1036            1 :         }
    1037              : 
    1038            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1039              :                             , advgetopt::line_continuation_t::line_continuation_rfc_822
    1040              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1041              :                             , advgetopt::COMMENT_SHELL
    1042            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1043              : 
    1044            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1045              : 
    1046            1 :         CATCH_REQUIRE(setup.is_valid());
    1047            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_rfc_822);
    1048            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1049            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1050            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1051              : 
    1052            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1053              : 
    1054            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1055            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1056            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1057            1 :         CATCH_REQUIRE(file->get_parameters().size() == 9);
    1058              : 
    1059            3 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1060            3 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1061            3 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1062            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("and-continue"));
    1063            3 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1064            3 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
    1065            3 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1066            3 :         CATCH_REQUIRE(file->has_parameter("&since"));
    1067            3 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1068            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    1069              : 
    1070            3 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1071            3 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start herecontinue=there");
    1072            3 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &and-continue=on next line");
    1073            3 :         CATCH_REQUIRE(file->get_parameter("and-continue") == std::string());
    1074            3 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
    1075            3 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
    1076            3 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
    1077            3 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
    1078            3 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1079            3 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1080            1 :     }
    1081            7 :     CATCH_END_SECTION()
    1082              : 
    1083            7 :     CATCH_START_SECTION("config_line_continuation_tests: msdos")
    1084              :     {
    1085            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "msdos");
    1086              : 
    1087              :         {
    1088            1 :             std::ofstream config_file;
    1089            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1090            1 :             CATCH_REQUIRE(config_file.good());
    1091              :             config_file <<
    1092              :                 "# Auto-generated\n"
    1093              :                 "normal=param\n"
    1094              :                 "\n"
    1095              :                 "rfc-822=start here\n"
    1096              :                 "  continue=there\n"
    1097              :                 "\n"
    1098              :                 "msdos=end with &\n"
    1099              :                 "  and-continue=on next line\n"
    1100              :                 "\n"
    1101              :                 "unix=end with \\\n"
    1102              :                 "to-continue=like this\n"
    1103              :                 "\n"
    1104              :                 "fortran=fortran is funny\n"
    1105              :                 "&since=it starts with an & on the following line\n"
    1106              :                 "\n"
    1107              :                 "semicolon=this ends with\n"
    1108            1 :                 "a=semi-colon only;\n"
    1109              :             ;
    1110            1 :         }
    1111              : 
    1112            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1113              :                             , advgetopt::line_continuation_t::line_continuation_msdos
    1114              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1115              :                             , advgetopt::COMMENT_SHELL
    1116            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1117              : 
    1118            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1119              : 
    1120            1 :         CATCH_REQUIRE(setup.is_valid());
    1121            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_msdos);
    1122            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1123            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1124            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1125              : 
    1126            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1127              : 
    1128            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1129            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1130            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1131            1 :         CATCH_REQUIRE(file->get_parameters().size() == 10);
    1132              : 
    1133            3 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1134            3 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1135            3 :         CATCH_REQUIRE(file->has_parameter("continue"));
    1136            3 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1137            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("and-continue"));
    1138            3 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1139            3 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
    1140            3 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1141            3 :         CATCH_REQUIRE(file->has_parameter("&since"));
    1142            3 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1143            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    1144              : 
    1145            3 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1146            3 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
    1147            3 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
    1148            3 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with   and-continue=on next line");
    1149            3 :         CATCH_REQUIRE(file->get_parameter("and-continue") == std::string());
    1150            3 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
    1151            3 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
    1152            3 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
    1153            3 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
    1154            3 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1155            3 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1156            1 :     }
    1157            7 :     CATCH_END_SECTION()
    1158              : 
    1159            7 :     CATCH_START_SECTION("config_line_continuation_tests: unix")
    1160              :     {
    1161            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "unix");
    1162              : 
    1163              :         {
    1164            1 :             std::ofstream config_file;
    1165            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1166            1 :             CATCH_REQUIRE(config_file.good());
    1167              :             config_file <<
    1168              :                 "# Auto-generated\n"
    1169              :                 "normal=param\n"
    1170              :                 "\n"
    1171              :                 "rfc-822=start here\n"
    1172              :                 "  continue=there\n"
    1173              :                 "\n"
    1174              :                 "msdos=end with &\n"
    1175              :                 "  and-continue=on next line\n"
    1176              :                 "\n"
    1177              :                 "unix=end with \\\n"
    1178              :                 "to-continue=like this\n"
    1179              :                 "\n"
    1180              :                 "fortran=fortran is funny\n"
    1181              :                 "&since=it starts with an & on the following line\n"
    1182              :                 "\n"
    1183              :                 "semicolon=this ends with\n"
    1184            1 :                 "a=semi-colon only;\n"
    1185              :             ;
    1186            1 :         }
    1187              : 
    1188            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1189              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1190              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1191              :                             , advgetopt::COMMENT_SHELL
    1192            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1193              : 
    1194            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1195              : 
    1196            1 :         CATCH_REQUIRE(setup.is_valid());
    1197            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1198            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1199            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1200            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1201              : 
    1202            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1203              : 
    1204            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1205            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1206            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1207            1 :         CATCH_REQUIRE(file->get_parameters().size() == 10);
    1208              : 
    1209            3 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1210            3 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1211            3 :         CATCH_REQUIRE(file->has_parameter("continue"));
    1212            3 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1213            3 :         CATCH_REQUIRE(file->has_parameter("and-continue"));
    1214            3 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1215            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("to-continue"));
    1216            3 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1217            3 :         CATCH_REQUIRE(file->has_parameter("&since"));
    1218            3 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1219            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    1220              : 
    1221            3 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1222            3 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
    1223            3 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
    1224            3 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &");
    1225            3 :         CATCH_REQUIRE(file->get_parameter("and-continue") == "on next line");
    1226            3 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with to-continue=like this");
    1227            3 :         CATCH_REQUIRE(file->get_parameter("to-continue") == std::string());
    1228            3 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
    1229            3 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
    1230            3 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1231            3 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1232            1 :     }
    1233            7 :     CATCH_END_SECTION()
    1234              : 
    1235            7 :     CATCH_START_SECTION("config_line_continuation_tests: fortran")
    1236              :     {
    1237            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "fortran");
    1238              : 
    1239              :         {
    1240            1 :             std::ofstream config_file;
    1241            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1242            1 :             CATCH_REQUIRE(config_file.good());
    1243              :             config_file <<
    1244              :                 "# Auto-generated\n"
    1245              :                 "normal=param\n"
    1246              :                 "\n"
    1247              :                 "rfc-822=start here\n"
    1248              :                 "  continue=there\n"
    1249              :                 "\n"
    1250              :                 "msdos=end with &\n"
    1251              :                 "  and-continue=on next line\n"
    1252              :                 "\n"
    1253              :                 "unix=end with \\\n"
    1254              :                 "to-continue=like this\n"
    1255              :                 "\n"
    1256              :                 "fortran=fortran is funny\n"
    1257              :                 "&since=it starts with an & on the following line\n"
    1258              :                 "\n"
    1259              :                 "semicolon=this ends with\n"
    1260            1 :                 "a=semi-colon only;\n"
    1261              :             ;
    1262            1 :         }
    1263              : 
    1264            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1265              :                             , advgetopt::line_continuation_t::line_continuation_fortran
    1266              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1267              :                             , advgetopt::COMMENT_SHELL
    1268            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1269              : 
    1270            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1271              : 
    1272            1 :         CATCH_REQUIRE(setup.is_valid());
    1273            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_fortran);
    1274            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1275            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1276            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1277              : 
    1278            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1279              : 
    1280            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1281            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1282            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1283            1 :         CATCH_REQUIRE(file->get_parameters().size() == 10);
    1284              : 
    1285            3 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1286            3 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1287            3 :         CATCH_REQUIRE(file->has_parameter("continue"));
    1288            3 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1289            3 :         CATCH_REQUIRE(file->has_parameter("and-continue"));
    1290            3 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1291            3 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
    1292            3 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1293            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("&since"));
    1294            3 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1295            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    1296              : 
    1297            3 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1298            3 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
    1299            3 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
    1300            3 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &");
    1301            3 :         CATCH_REQUIRE(file->get_parameter("and-continue") == "on next line");
    1302            3 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
    1303            3 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
    1304            3 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funnysince=it starts with an & on the following line");
    1305            3 :         CATCH_REQUIRE(file->get_parameter("&since") == std::string());
    1306            3 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1307            3 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1308            1 :     }
    1309            7 :     CATCH_END_SECTION()
    1310              : 
    1311            7 :     CATCH_START_SECTION("config_line_continuation_tests: semicolon")
    1312              :     {
    1313            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "semicolon");
    1314              : 
    1315              :         {
    1316            1 :             std::ofstream config_file;
    1317            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1318            1 :             CATCH_REQUIRE(config_file.good());
    1319              :             config_file <<
    1320              :                 "# Auto-generated\r\n"
    1321              :                 "normal=param\r\n"
    1322              :                 "\r\n"
    1323              :                 "rfc-822=start here\r\n"
    1324              :                 "  continue=there\r\n"
    1325              :                 "\r\n"
    1326              :                 "msdos=end with &\r"
    1327              :                 "  and-continue=on next line\r\n"
    1328              :                 "\r\n"
    1329              :                 "unix=end with \\\r\n"
    1330              :                 "to-continue=like this\r"
    1331              :                 "\r\n"
    1332              :                 "fortran=fortran is funny\r\n"
    1333              :                 "&since=it starts with an & on the following line\r\n"
    1334              :                 "\r"
    1335              :                 "semicolon=this ends with\r\n"
    1336            1 :                 "a=semi-colon only;\r\n"
    1337              :             ;
    1338            1 :         }
    1339              : 
    1340            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1341              :                             , advgetopt::line_continuation_t::line_continuation_semicolon
    1342              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1343              :                             , advgetopt::COMMENT_SHELL
    1344            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1345              : 
    1346            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1347              : 
    1348            1 :         CATCH_REQUIRE(setup.is_valid());
    1349            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_semicolon);
    1350            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1351            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1352            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1353              : 
    1354            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1355              : 
    1356            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1357            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1358            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1359            1 :         CATCH_REQUIRE(file->get_parameters().size() == 1);
    1360              : 
    1361            3 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1362            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("rfc-822"));
    1363            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("continue"));
    1364            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("msdos"));
    1365            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("and-continue"));
    1366            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("unix"));
    1367            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("to-continue"));
    1368            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("fortran"));
    1369            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("&since"));
    1370            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("semicolon"));
    1371            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("a"));
    1372              : 
    1373            5 :         CATCH_REQUIRE(file->get_parameter("normal") == std::string("param\n"
    1374              : "\n"
    1375              : "rfc-822=start here\n"
    1376              : "  continue=there\n"
    1377              : "\n"
    1378              : "msdos=end with &\n"
    1379              : "  and-continue=on next line\n"
    1380              : "\n"
    1381              : "unix=end with \\\n"
    1382              : "to-continue=like this\n"
    1383              : "\n"
    1384              : "fortran=fortran is funny\n"
    1385              : "&since=it starts with an & on the following line\n"
    1386              : "\n"
    1387              : "semicolon=this ends with\n"
    1388              : "a=semi-colon only"));
    1389            3 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == std::string());
    1390            3 :         CATCH_REQUIRE(file->get_parameter("continue") == std::string());
    1391            3 :         CATCH_REQUIRE(file->get_parameter("msdos") == std::string());
    1392            3 :         CATCH_REQUIRE(file->get_parameter("and-continue") == std::string());
    1393            3 :         CATCH_REQUIRE(file->get_parameter("unix") == std::string());
    1394            3 :         CATCH_REQUIRE(file->get_parameter("to-continue") == std::string());
    1395            3 :         CATCH_REQUIRE(file->get_parameter("fortran") == std::string());
    1396            3 :         CATCH_REQUIRE(file->get_parameter("&since") == std::string());
    1397            3 :         CATCH_REQUIRE(file->get_parameter("semicolon") == std::string());
    1398            3 :         CATCH_REQUIRE(file->get_parameter("a") == std::string());
    1399            1 :     }
    1400            7 :     CATCH_END_SECTION()
    1401            7 : }
    1402              : 
    1403              : 
    1404              : 
    1405            4 : CATCH_TEST_CASE("config_assignment_operator_tests", "[config][getopt][valid]")
    1406              : {
    1407            4 :     CATCH_START_SECTION("config_assignment_operator_tests: equal")
    1408              :     {
    1409            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "equal");
    1410              : 
    1411              :         {
    1412            1 :             std::ofstream config_file;
    1413            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1414            1 :             CATCH_REQUIRE(config_file.good());
    1415              :             config_file <<
    1416              :                 "# Auto-generated\n"
    1417              :                 "equal=value\n"
    1418              :                 "\n"
    1419              :                 "name_value=127\n"
    1420              :                 "\n"
    1421            1 :                 "and=no operator\n"
    1422              :             ;
    1423            1 :         }
    1424              : 
    1425            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1426              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1427              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1428              :                             , advgetopt::COMMENT_SHELL
    1429            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1430              : 
    1431            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1432              : 
    1433            1 :         CATCH_REQUIRE(setup.is_valid());
    1434            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1435            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1436            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1437            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1438              : 
    1439            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1440              : 
    1441            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1442            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1443            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1444            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1445              : 
    1446            3 :         CATCH_REQUIRE(file->has_parameter("equal"));
    1447            3 :         CATCH_REQUIRE(file->has_parameter("name-value"));
    1448            3 :         CATCH_REQUIRE(file->has_parameter("and"));
    1449              : 
    1450            3 :         CATCH_REQUIRE(file->get_parameter("equal") == "value");
    1451            3 :         CATCH_REQUIRE(file->get_parameter("name-value") == "127");
    1452            3 :         CATCH_REQUIRE(file->get_parameter("and") == "no operator");
    1453            1 :     }
    1454            4 :     CATCH_END_SECTION()
    1455              : 
    1456            4 :     CATCH_START_SECTION("config_assignment_operator_tests: colon")
    1457              :     {
    1458            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "colon");
    1459              : 
    1460              :         {
    1461            1 :             std::ofstream config_file;
    1462            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1463            1 :             CATCH_REQUIRE(config_file.good());
    1464              :             config_file <<
    1465              :                 "# Auto-generated\n"
    1466              :                 "equal_value\n"
    1467              :                 "\n"
    1468              :                 "name:value=127\n"
    1469              :                 "\n"
    1470            1 :                 "and_no-operator\n"
    1471              :             ;
    1472            1 :         }
    1473              : 
    1474            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1475              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1476              :                             , advgetopt::ASSIGNMENT_OPERATOR_COLON
    1477              :                             , advgetopt::COMMENT_SHELL
    1478            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1479              : 
    1480            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1481              : 
    1482            1 :         CATCH_REQUIRE(setup.is_valid());
    1483            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1484            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
    1485            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1486            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1487              : 
    1488            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1489              : 
    1490            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1491            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1492            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1493            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1494              : 
    1495            3 :         CATCH_REQUIRE(file->has_parameter("equal-value"));
    1496            3 :         CATCH_REQUIRE(file->has_parameter("name"));
    1497            3 :         CATCH_REQUIRE(file->has_parameter("and-no-operator"));
    1498              : 
    1499            3 :         CATCH_REQUIRE(file->get_parameter("equal-value") == std::string());
    1500            3 :         CATCH_REQUIRE(file->get_parameter("name") == "value=127");
    1501            3 :         CATCH_REQUIRE(file->get_parameter("and-no-operator") == std::string());
    1502            1 :     }
    1503            4 :     CATCH_END_SECTION()
    1504              : 
    1505            4 :     CATCH_START_SECTION("config_assignment_operator_tests: space")
    1506              :     {
    1507            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "space");
    1508              : 
    1509              :         {
    1510            1 :             std::ofstream config_file;
    1511            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1512            1 :             CATCH_REQUIRE(config_file.good());
    1513              :             config_file <<
    1514              :                 "# Auto-generated\n"
    1515              :                 "equal-value\n"
    1516              :                 "\n"
    1517              :                 "name 127\n"
    1518              :                 "\n"
    1519            1 :                 "and-no operator\n"
    1520              :             ;
    1521            1 :         }
    1522              : 
    1523            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1524              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1525              :                             , advgetopt::ASSIGNMENT_OPERATOR_SPACE
    1526              :                             , advgetopt::COMMENT_SHELL
    1527            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1528              : 
    1529            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1530              : 
    1531            1 :         CATCH_REQUIRE(setup.is_valid());
    1532            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1533            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_SPACE);
    1534            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1535            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1536              : 
    1537            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1538              : 
    1539            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1540            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1541            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1542            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1543              : 
    1544            3 :         CATCH_REQUIRE(file->has_parameter("equal-value"));
    1545            3 :         CATCH_REQUIRE(file->has_parameter("name"));
    1546            3 :         CATCH_REQUIRE(file->has_parameter("and-no"));
    1547              : 
    1548            3 :         CATCH_REQUIRE(file->get_parameter("equal-value") == std::string());
    1549            3 :         CATCH_REQUIRE(file->get_parameter("name") == "127");
    1550            3 :         CATCH_REQUIRE(file->get_parameter("and-no") == "operator");
    1551            1 :     }
    1552            4 :     CATCH_END_SECTION()
    1553              : 
    1554            4 :     CATCH_START_SECTION("config_assignment_operator_tests: equal_colon_and_space")
    1555              :     {
    1556            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "all");
    1557              : 
    1558              :         {
    1559            1 :             std::ofstream config_file;
    1560            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1561            1 :             CATCH_REQUIRE(config_file.good());
    1562              :             config_file <<
    1563              :                 "# Auto-generated\n"
    1564              :                 "equal=value\n"
    1565              :                 "\n"
    1566              :                 "name: 127\n"
    1567              :                 "\n"
    1568            1 :                 "and no operator\n"
    1569              :             ;
    1570            1 :         }
    1571              : 
    1572            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1573              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1574              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1575              :                             | advgetopt::ASSIGNMENT_OPERATOR_COLON
    1576              :                             | advgetopt::ASSIGNMENT_OPERATOR_SPACE
    1577              :                             , advgetopt::COMMENT_SHELL
    1578            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1579              : 
    1580            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1581              : 
    1582            1 :         CATCH_REQUIRE(setup.is_valid());
    1583            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1584            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == (advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1585              :                                                        | advgetopt::ASSIGNMENT_OPERATOR_COLON
    1586              :                                                        | advgetopt::ASSIGNMENT_OPERATOR_SPACE));
    1587            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1588            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1589              : 
    1590            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1591              : 
    1592            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1593            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1594            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1595            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1596              : 
    1597            3 :         CATCH_REQUIRE(file->has_parameter("equal"));
    1598            3 :         CATCH_REQUIRE(file->has_parameter("name"));
    1599            3 :         CATCH_REQUIRE(file->has_parameter("and"));
    1600              : 
    1601            3 :         CATCH_REQUIRE(file->get_parameter("equal") == "value");
    1602            3 :         CATCH_REQUIRE(file->get_parameter("name") == "127");
    1603            3 :         CATCH_REQUIRE(file->get_parameter("and") == "no operator");
    1604            1 :     }
    1605            4 :     CATCH_END_SECTION()
    1606            4 : }
    1607              : 
    1608              : 
    1609              : 
    1610              : 
    1611              : 
    1612            4 : CATCH_TEST_CASE("config_comment_tests", "[config][getopt][valid]")
    1613              : {
    1614            4 :     CATCH_START_SECTION("config_comment_tests: ini comment")
    1615              :     {
    1616            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "ini");
    1617              : 
    1618              :         {
    1619            1 :             std::ofstream config_file;
    1620            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1621            1 :             CATCH_REQUIRE(config_file.good());
    1622              :             config_file <<
    1623              :                 "; Auto-generated\n"
    1624              :                 "ini=comment\n"
    1625              :                 ";ignore=this one\n"
    1626              :                 "is=the semi-colon\n"
    1627              :                 ";continuation=with Unix\\\n"
    1628              :                 "also=works for\\\n"
    1629            1 :                 "comments\n"
    1630              :             ;
    1631            1 :         }
    1632              : 
    1633            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1634              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1635              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1636              :                             , advgetopt::COMMENT_INI
    1637            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1638              : 
    1639            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1640              : 
    1641            1 :         CATCH_REQUIRE(setup.is_valid());
    1642            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1643            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1644            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_INI);
    1645            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1646              : 
    1647            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1648              : 
    1649            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1650            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1651            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1652            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    1653              : 
    1654            3 :         CATCH_REQUIRE(file->has_parameter("ini"));
    1655            3 :         CATCH_REQUIRE(file->has_parameter("is"));
    1656              : 
    1657            3 :         CATCH_REQUIRE(file->get_parameter("ini") == "comment");
    1658            3 :         CATCH_REQUIRE(file->get_parameter("is") == "the semi-colon");
    1659            1 :     }
    1660            4 :     CATCH_END_SECTION()
    1661              : 
    1662            4 :     CATCH_START_SECTION("config_comment_tests: shell comment")
    1663              :     {
    1664            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "shell");
    1665              : 
    1666              :         {
    1667            1 :             std::ofstream config_file;
    1668            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1669            1 :             CATCH_REQUIRE(config_file.good());
    1670              :             config_file <<
    1671              :                 "# Auto-generated\n"
    1672              :                 "shell=comment\n"
    1673              :                 "#ignore=this one\n"
    1674              :                 "is=the hash (`#`) character\n"
    1675              :                 "#continuation=with Unix\\\n"
    1676              :                 "also=works for\\\n"
    1677            1 :                 "comments\n"
    1678              :             ;
    1679            1 :         }
    1680              : 
    1681            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1682              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1683              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1684              :                             , advgetopt::COMMENT_SHELL
    1685            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1686              : 
    1687            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1688              : 
    1689            1 :         CATCH_REQUIRE(setup.is_valid());
    1690            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1691            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1692            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1693            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1694              : 
    1695            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1696              : 
    1697            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1698            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1699            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1700            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    1701              : 
    1702            3 :         CATCH_REQUIRE(file->has_parameter("shell"));
    1703            3 :         CATCH_REQUIRE(file->has_parameter("is"));
    1704              : 
    1705            3 :         CATCH_REQUIRE(file->get_parameter("shell") == "comment");
    1706            3 :         CATCH_REQUIRE(file->get_parameter("is") == "the hash (`#`) character");
    1707            1 :     }
    1708            4 :     CATCH_END_SECTION()
    1709              : 
    1710            4 :     CATCH_START_SECTION("config_comment_tests: C++ comment")
    1711              :     {
    1712            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "cpp");
    1713              : 
    1714              :         {
    1715            1 :             std::ofstream config_file;
    1716            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1717            1 :             CATCH_REQUIRE(config_file.good());
    1718              :             config_file <<
    1719              :                 "// Auto-generated\n"
    1720              :                 "cpp=comment\n"
    1721              :                 "//ignore=this one\n"
    1722              :                 "is=the double slash (`//`)\n"
    1723              :                 "//continuation=with Unix\\\n"
    1724              :                 "also=works for\\\n"
    1725            1 :                 "comments\n"
    1726              :             ;
    1727            1 :         }
    1728              : 
    1729            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1730              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1731              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1732              :                             , advgetopt::COMMENT_CPP
    1733            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1734              : 
    1735            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1736              : 
    1737            1 :         CATCH_REQUIRE(setup.is_valid());
    1738            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1739            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1740            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_CPP);
    1741            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1742              : 
    1743            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1744              : 
    1745            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1746            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1747            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1748            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    1749              : 
    1750            3 :         CATCH_REQUIRE(file->has_parameter("cpp"));
    1751            3 :         CATCH_REQUIRE(file->has_parameter("is"));
    1752              : 
    1753            3 :         CATCH_REQUIRE(file->get_parameter("cpp") == "comment");
    1754            3 :         CATCH_REQUIRE(file->get_parameter("is") == "the double slash (`//`)");
    1755            1 :     }
    1756            4 :     CATCH_END_SECTION()
    1757              : 
    1758            4 :     CATCH_START_SECTION("config_comment_tests: all three comments")
    1759              :     {
    1760            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "all-comments");
    1761              : 
    1762              :         {
    1763            1 :             std::ofstream config_file;
    1764            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1765            1 :             CATCH_REQUIRE(config_file.good());
    1766              :             config_file <<
    1767              :                 "// Auto-generated\n"
    1768              :                 "all=comments\n"
    1769              :                 ";ignore=this one\n"
    1770              :                 "together=for powerful config support\n"
    1771              :                 "#continuation=with Unix\\\n"
    1772              :                 "also=works for\\\n"
    1773              :                 "comments\n"
    1774              :                 "but=maybe\n"
    1775              :                 ";we=should\\\n"
    1776              :                 "test=continuation\n"
    1777              :                 "//with=each\\\n"
    1778            1 :                 "each=type of comment\n"
    1779              :             ;
    1780            1 :         }
    1781              : 
    1782            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1783              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1784              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1785              :                             , advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL | advgetopt::COMMENT_CPP
    1786            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1787              : 
    1788            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1789              : 
    1790            1 :         CATCH_REQUIRE(setup.is_valid());
    1791            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1792            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1793            1 :         CATCH_REQUIRE(setup.get_comment() == (advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL | advgetopt::COMMENT_CPP));
    1794            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1795              : 
    1796            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1797              : 
    1798            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1799            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1800            1 :         CATCH_REQUIRE(file->get_sections().empty());
    1801            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1802              : 
    1803            3 :         CATCH_REQUIRE(file->has_parameter("all"));
    1804            3 :         CATCH_REQUIRE(file->has_parameter("together"));
    1805            3 :         CATCH_REQUIRE(file->has_parameter("but"));
    1806              : 
    1807            3 :         CATCH_REQUIRE(file->get_parameter("all") == "comments");
    1808            3 :         CATCH_REQUIRE(file->get_parameter("together") == "for powerful config support");
    1809            3 :         CATCH_REQUIRE(file->get_parameter("but") == "maybe");
    1810            1 :     }
    1811            4 :     CATCH_END_SECTION()
    1812            4 : }
    1813              : 
    1814              : 
    1815              : 
    1816              : 
    1817              : 
    1818            7 : CATCH_TEST_CASE("config_section_tests", "[config][getopt][valid]")
    1819              : {
    1820            7 :     CATCH_START_SECTION("config_section_tests: section operator c (.)")
    1821              :     {
    1822            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-c");
    1823              : 
    1824              :         {
    1825            1 :             std::ofstream config_file;
    1826            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1827            1 :             CATCH_REQUIRE(config_file.good());
    1828              :             config_file <<
    1829              :                 "# Auto-generated\n"
    1830              :                 "a=color\n"
    1831              :                 "a.b=red\n"
    1832              :                 "a.b.c=122\n"
    1833              :                 "m=size\n"
    1834              :                 "z=edge\n"
    1835              :                 "z.b=line\n"
    1836            1 :                 "z.b.c=12.72\n"
    1837              :             ;
    1838            1 :         }
    1839              : 
    1840            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1841              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1842              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1843              :                             , advgetopt::COMMENT_SHELL
    1844            1 :                             , advgetopt::SECTION_OPERATOR_C);
    1845              : 
    1846            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1847              : 
    1848            1 :         CATCH_REQUIRE(setup.is_valid());
    1849            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1850            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1851            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1852            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    1853              : 
    1854            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1855              : 
    1856            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1857            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1858              : 
    1859            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    1860            1 :         CATCH_REQUIRE(sections.size() == 4);
    1861            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    1862            3 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    1863            3 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    1864            3 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    1865              : 
    1866            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    1867              : 
    1868            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    1869            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    1870            3 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    1871            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    1872            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    1873            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    1874            3 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    1875              : 
    1876            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    1877            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    1878            3 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "122");
    1879            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    1880            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    1881            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    1882            3 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    1883            1 :     }
    1884            7 :     CATCH_END_SECTION()
    1885              : 
    1886            7 :     CATCH_START_SECTION("config_section_tests: section operator c++ (::)")
    1887              :     {
    1888            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-cpp");
    1889              : 
    1890              :         {
    1891            1 :             std::ofstream config_file;
    1892            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1893            1 :             CATCH_REQUIRE(config_file.good());
    1894              :             config_file <<
    1895              :                 "# Auto-generated\n"
    1896              :                 "a=color\n"
    1897              :                 "a::b=red\n"
    1898              :                 "a::b::c=122\n"
    1899              :                 "m=size\n"
    1900              :                 "z=edge\n"
    1901              :                 "z::b=line\n"
    1902            1 :                 "z::b::c=12.72\n"
    1903              :             ;
    1904            1 :         }
    1905              : 
    1906            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1907              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1908              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1909              :                             , advgetopt::COMMENT_SHELL
    1910            1 :                             , advgetopt::SECTION_OPERATOR_CPP);
    1911              : 
    1912            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1913              : 
    1914            1 :         CATCH_REQUIRE(setup.is_valid());
    1915            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1916            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1917            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1918            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_CPP);
    1919              : 
    1920            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1921              : 
    1922            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1923            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1924              : 
    1925            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    1926            1 :         CATCH_REQUIRE(sections.size() == 4);
    1927            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    1928            3 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    1929            3 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    1930            3 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    1931              : 
    1932            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    1933              : 
    1934            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    1935            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    1936            3 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    1937            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    1938            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    1939            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    1940            3 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    1941              : 
    1942            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    1943            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    1944            3 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "122");
    1945            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    1946            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    1947            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    1948            3 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    1949            1 :     }
    1950            7 :     CATCH_END_SECTION()
    1951              : 
    1952            7 :     CATCH_START_SECTION("config_section_tests: section operator block ({ ... })")
    1953              :     {
    1954            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-block");
    1955              : 
    1956              :         {
    1957            1 :             std::ofstream config_file;
    1958            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1959            1 :             CATCH_REQUIRE(config_file.good());
    1960              :             config_file <<
    1961              :                 "# Auto-generated\n"
    1962              :                 "a=color\n"
    1963              :                 "a {\n"
    1964              :                 "  b=red\n"
    1965              :                 "  b {\n"
    1966              :                 "    c=122\n"
    1967              :                 "  }\n"
    1968              :                 "}\n"
    1969              :                 "m=size\n"
    1970              :                 "z=edge\n"
    1971              :                 "z {\n"
    1972              :                 "  b {\n"
    1973              :                 "    c=12.72\n"
    1974              :                 "  }\n"
    1975              :                 "  b=line\n"
    1976            1 :                 "}\n"
    1977              :             ;
    1978            1 :         }
    1979              : 
    1980            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1981              :                             , advgetopt::line_continuation_t::line_continuation_unix
    1982              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1983              :                             , advgetopt::COMMENT_SHELL
    1984            1 :                             , advgetopt::SECTION_OPERATOR_BLOCK);
    1985              : 
    1986            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1987              : 
    1988            1 :         CATCH_REQUIRE(setup.is_valid());
    1989            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1990            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1991            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1992            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_BLOCK);
    1993              : 
    1994            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1995              : 
    1996            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1997            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1998              : 
    1999            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2000            1 :         CATCH_REQUIRE(sections.size() == 4);
    2001            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    2002            3 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    2003            3 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    2004            3 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    2005              : 
    2006            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    2007              : 
    2008            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    2009            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    2010            3 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    2011            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    2012            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    2013            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    2014            3 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    2015              : 
    2016            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2017            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    2018            3 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "122");
    2019            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    2020            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    2021            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    2022            3 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    2023            1 :     }
    2024            7 :     CATCH_END_SECTION()
    2025              : 
    2026            7 :     CATCH_START_SECTION("config_section_tests: section operator ini file ([...])")
    2027              :     {
    2028            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-ini-file");
    2029              : 
    2030              :         {
    2031            1 :             std::ofstream config_file;
    2032            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2033            1 :             CATCH_REQUIRE(config_file.good());
    2034              :             config_file <<
    2035              :                 "# Auto-generated\n"
    2036              :                 "a=color\n"
    2037              :                 "[a]\n"
    2038              :                 "b=red\n"
    2039              :                 "b-c=122\n"
    2040              :                 "[]\n"
    2041              :                 "m=size\n"
    2042              :                 "z=edge\n"
    2043              :                 "[z] # we allow comments here\n"
    2044              :                 "b=line\n"
    2045              :                 "b-c=12.72\n"
    2046              :                 "[p]#nospacenecessary\n"
    2047              :                 "b=comment\n"
    2048            1 :                 "b-c=allowed\n"
    2049              :             ;
    2050            1 :         }
    2051              : 
    2052            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2053              :                             , advgetopt::line_continuation_t::line_continuation_unix
    2054              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2055              :                             , advgetopt::COMMENT_SHELL
    2056            1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE);
    2057              : 
    2058            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2059              : 
    2060            1 :         CATCH_REQUIRE(setup.is_valid());
    2061            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    2062            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2063            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2064            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_INI_FILE);
    2065              : 
    2066            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2067              : 
    2068            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2069            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2070              : 
    2071            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2072            1 :         CATCH_REQUIRE(sections.size() == 3);
    2073            3 :         CATCH_REQUIRE(sections.find("a") != sections.end());
    2074            3 :         CATCH_REQUIRE(sections.find("z") != sections.end());
    2075            3 :         CATCH_REQUIRE(sections.find("p") != sections.end());
    2076              : 
    2077            1 :         CATCH_REQUIRE(file->get_parameters().size() == 9);
    2078              : 
    2079            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    2080            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    2081            3 :         CATCH_REQUIRE(file->has_parameter("a::b-c"));
    2082            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    2083            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    2084            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    2085            3 :         CATCH_REQUIRE(file->has_parameter("z::b-c"));
    2086            3 :         CATCH_REQUIRE(file->has_parameter("p::b"));
    2087            3 :         CATCH_REQUIRE(file->has_parameter("p::b-c"));
    2088              : 
    2089            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2090            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    2091            3 :         CATCH_REQUIRE(file->get_parameter("a::b-c") == "122");
    2092            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    2093            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    2094            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    2095            3 :         CATCH_REQUIRE(file->get_parameter("z::b-c") == "12.72");
    2096            3 :         CATCH_REQUIRE(file->get_parameter("p::b") == "comment");
    2097            3 :         CATCH_REQUIRE(file->get_parameter("p::b-c") == "allowed");
    2098            1 :     }
    2099            7 :     CATCH_END_SECTION()
    2100              : 
    2101            7 :     CATCH_START_SECTION("config_section_tests: section operator ini-file & c++")
    2102              :     {
    2103            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-double");
    2104              : 
    2105              :         {
    2106            1 :             std::ofstream config_file;
    2107            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2108            1 :             CATCH_REQUIRE(config_file.good());
    2109              :             config_file <<
    2110              :                 "# Auto-generated\n"
    2111              :                 "[a]\n"
    2112              :                 "b=red\n"
    2113              :                 "b::c=209\n"
    2114              :                 "::h=high\n"
    2115              :                 "m=size\n"
    2116              :                 "[z]\n"
    2117              :                 "z=edge\n"
    2118              :                 "::b=line\n"
    2119            1 :                 "z::b::c=17.92\n"
    2120              :             ;
    2121            1 :         }
    2122              : 
    2123            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2124              :                             , advgetopt::line_continuation_t::line_continuation_unix
    2125              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2126              :                             , advgetopt::COMMENT_SHELL
    2127            1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE | advgetopt::SECTION_OPERATOR_CPP);
    2128              : 
    2129            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2130              : 
    2131            1 :         CATCH_REQUIRE(setup.is_valid());
    2132            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    2133            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2134            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2135            1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_INI_FILE | advgetopt::SECTION_OPERATOR_CPP));
    2136              : 
    2137            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2138              : 
    2139            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2140            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2141              : 
    2142            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2143            1 :         CATCH_REQUIRE(sections.size() == 4);
    2144            3 :         CATCH_REQUIRE(sections.find("a")       != sections.end());
    2145            3 :         CATCH_REQUIRE(sections.find("a::b")    != sections.end());
    2146            3 :         CATCH_REQUIRE(sections.find("z")       != sections.end());
    2147            3 :         CATCH_REQUIRE(sections.find("z::z::b") != sections.end());
    2148              : 
    2149            1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    2150              : 
    2151            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    2152            3 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    2153            3 :         CATCH_REQUIRE(file->has_parameter("h"));
    2154            3 :         CATCH_REQUIRE(file->has_parameter("a::m"));
    2155            3 :         CATCH_REQUIRE(file->has_parameter("z::z"));
    2156            3 :         CATCH_REQUIRE(file->has_parameter("b"));
    2157            3 :         CATCH_REQUIRE(file->has_parameter("z::z::b::c"));
    2158              : 
    2159            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    2160            3 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "209");
    2161            3 :         CATCH_REQUIRE(file->get_parameter("h") == "high");
    2162            3 :         CATCH_REQUIRE(file->get_parameter("a::m") == "size");
    2163            3 :         CATCH_REQUIRE(file->get_parameter("z::z") == "edge");
    2164            3 :         CATCH_REQUIRE(file->get_parameter("b") == "line");
    2165            3 :         CATCH_REQUIRE(file->get_parameter("z::z::b::c") == "17.92");
    2166            1 :     }
    2167            7 :     CATCH_END_SECTION()
    2168              : 
    2169            7 :     CATCH_START_SECTION("config_section_tests: section of variables ([variables])")
    2170              :     {
    2171              :         // in a config file variables are not auto-managed
    2172              :         //
    2173            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-variables", "section-with-variables");
    2174              : 
    2175              :         {
    2176            1 :             std::ofstream config_file;
    2177            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2178            1 :             CATCH_REQUIRE(config_file.good());
    2179              :             config_file <<
    2180              :                 "# Auto-generated\n"
    2181              :                 "edge=${blue}\n"
    2182              :                 "background=${white}\n"
    2183              :                 "foreground=${black}\n"
    2184              :                 "[error]\n"
    2185              :                 "edge=${red}\n"
    2186              :                 "background=${gray}\n"
    2187              :                 "[variables]\n"
    2188              :                 "red=\"#ff0000\"\n"
    2189              :                 "green=\"#00ff00\"\n"
    2190              :                 "blue=\"#0000ff\"\n"
    2191              :                 "no_color=\"#000000\"\n"
    2192              :                 "black=${no_color}\n"
    2193              :                 "white=\"#ffffff\"\n"
    2194            1 :                 "gray=\"#aaaaaa\"\n"
    2195              :             ;
    2196            1 :         }
    2197              : 
    2198            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2199              :                             , advgetopt::line_continuation_t::line_continuation_unix
    2200              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2201              :                             , advgetopt::COMMENT_SHELL
    2202            1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE);
    2203              : 
    2204            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2205              : 
    2206            1 :         CATCH_REQUIRE(setup.is_valid());
    2207            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    2208            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2209            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2210            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_INI_FILE);
    2211              : 
    2212            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2213              : 
    2214            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2215            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2216              : 
    2217            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2218            1 :         CATCH_REQUIRE(sections.size() == 2);
    2219            3 :         CATCH_REQUIRE(sections.find("error") != sections.end());
    2220            3 :         CATCH_REQUIRE(sections.find("variables") != sections.end());
    2221              : 
    2222            1 :         CATCH_REQUIRE(file->get_parameters().size() == 12);
    2223              : 
    2224            3 :         CATCH_REQUIRE(file->has_parameter("edge"));
    2225            3 :         CATCH_REQUIRE(file->has_parameter("background"));
    2226            3 :         CATCH_REQUIRE(file->has_parameter("foreground"));
    2227            3 :         CATCH_REQUIRE(file->has_parameter("error::edge"));
    2228            3 :         CATCH_REQUIRE(file->has_parameter("error::background"));
    2229            3 :         CATCH_REQUIRE(file->has_parameter("variables::red"));
    2230            3 :         CATCH_REQUIRE(file->has_parameter("variables::green"));
    2231            3 :         CATCH_REQUIRE(file->has_parameter("variables::blue"));
    2232            3 :         CATCH_REQUIRE(file->has_parameter("variables::no_color"));
    2233            3 :         CATCH_REQUIRE(file->has_parameter("variables::black"));
    2234            3 :         CATCH_REQUIRE(file->has_parameter("variables::white"));
    2235            3 :         CATCH_REQUIRE(file->has_parameter("variables::gray"));
    2236              : 
    2237              :         // without a variables attached, we get the raw (original) data back
    2238              :         //
    2239            3 :         CATCH_REQUIRE(file->get_parameter("edge") == "${blue}");
    2240            3 :         CATCH_REQUIRE(file->get_parameter("background") == "${white}");
    2241            3 :         CATCH_REQUIRE(file->get_parameter("foreground") == "${black}");
    2242            3 :         CATCH_REQUIRE(file->get_parameter("error::edge") == "${red}");
    2243            3 :         CATCH_REQUIRE(file->get_parameter("error::background") == "${gray}");
    2244            3 :         CATCH_REQUIRE(file->get_parameter("variables::red") == "#ff0000");
    2245            3 :         CATCH_REQUIRE(file->get_parameter("variables::green") == "#00ff00");
    2246            3 :         CATCH_REQUIRE(file->get_parameter("variables::blue") == "#0000ff");
    2247            3 :         CATCH_REQUIRE(file->get_parameter("variables::no_color") == "#000000");
    2248            3 :         CATCH_REQUIRE(file->get_parameter("variables::black") == "${no_color}");
    2249            3 :         CATCH_REQUIRE(file->get_parameter("variables::white") == "#ffffff");
    2250            3 :         CATCH_REQUIRE(file->get_parameter("variables::gray") == "#aaaaaa");
    2251              : 
    2252              :         // transform the "[variables]" section to variables
    2253              :         //
    2254            1 :         advgetopt::variables::pointer_t vars(std::make_shared<advgetopt::variables>());
    2255            3 :         CATCH_REQUIRE(file->section_to_variables("variables", vars) == 7);
    2256            1 :         file->set_variables(vars);
    2257            1 :         CATCH_REQUIRE(file->get_variables() == vars);
    2258              : 
    2259            1 :         sections = file->get_sections();
    2260            1 :         CATCH_REQUIRE(sections.size() == 1);
    2261            3 :         CATCH_REQUIRE(sections.find("error") != sections.end());
    2262            3 :         CATCH_REQUIRE(sections.find("variables") == sections.end());
    2263              : 
    2264            1 :         CATCH_REQUIRE(file->get_parameters().size() == 5);
    2265              : 
    2266            3 :         CATCH_REQUIRE(file->has_parameter("edge"));
    2267            3 :         CATCH_REQUIRE(file->has_parameter("background"));
    2268            3 :         CATCH_REQUIRE(file->has_parameter("foreground"));
    2269            3 :         CATCH_REQUIRE(file->has_parameter("error::edge"));
    2270            3 :         CATCH_REQUIRE(file->has_parameter("error::background"));
    2271            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::red"));
    2272            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::green"));
    2273            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::blue"));
    2274            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::no_color"));
    2275            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::black"));
    2276            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::white"));
    2277            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::gray"));
    2278              : 
    2279            3 :         CATCH_REQUIRE(file->get_parameter("edge") == "#0000ff");
    2280            3 :         CATCH_REQUIRE(file->get_parameter("background") == "#ffffff");
    2281            3 :         CATCH_REQUIRE(file->get_parameter("foreground") == "#000000");
    2282            3 :         CATCH_REQUIRE(file->get_parameter("error::edge") == "#ff0000");
    2283            3 :         CATCH_REQUIRE(file->get_parameter("error::background") == "#aaaaaa");
    2284            1 :     }
    2285            7 :     CATCH_END_SECTION()
    2286              : 
    2287            7 :     CATCH_START_SECTION("config_section_tests: command line with .conf including section of variables ([variables])")
    2288              :     {
    2289              :         // in a config file variables are not auto-managed
    2290              :         //
    2291            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("command-line-and-section-variables", "command-section-with-variables");
    2292              : 
    2293              :         {
    2294            1 :             std::ofstream config_file;
    2295            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2296            1 :             CATCH_REQUIRE(config_file.good());
    2297              :             config_file <<
    2298              :                 "# Auto-generated\n"
    2299              :                 "edge=${blue}\n"
    2300              :                 "background=${white}\n"
    2301              :                 "foreground=${black}\n"
    2302              :                 "[error]\n"
    2303              :                 "edge=${red}\n"
    2304              :                 "background=${gray}\n"
    2305              :                 "[variables]\n"
    2306              :                 "red=\"#ff0000\"\n"
    2307              :                 "green=\"#00ff00\"\n"
    2308              :                 "blue=\"#0000ff\"\n"
    2309              :                 "no_color=\"#000000\"\n"
    2310              :                 "black=${no_color}\n"
    2311              :                 "orange=\"#80ff00\"\n"
    2312              :                 "white=\"#ffffff\"\n"
    2313            1 :                 "gray=\"#aaaaaa\"\n"
    2314              :             ;
    2315            1 :         }
    2316              : 
    2317            1 :         const advgetopt::option options[] =
    2318              :         {
    2319              :             advgetopt::define_option(
    2320              :                   advgetopt::Name("edge")
    2321              :                 , advgetopt::Flags(advgetopt::all_flags<
    2322              :                       advgetopt::GETOPT_FLAG_REQUIRED
    2323              :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2324              :             ),
    2325              :             advgetopt::define_option(
    2326              :                   advgetopt::Name("background")
    2327              :                 , advgetopt::Flags(advgetopt::all_flags<
    2328              :                       advgetopt::GETOPT_FLAG_REQUIRED
    2329              :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2330              :             ),
    2331              :             advgetopt::define_option(
    2332              :                   advgetopt::Name("foreground")
    2333              :                 , advgetopt::Flags(advgetopt::all_flags<
    2334              :                       advgetopt::GETOPT_FLAG_REQUIRED
    2335              :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2336              :             ),
    2337              :             advgetopt::define_option(
    2338              :                   advgetopt::Name("error::edge")
    2339              :                 , advgetopt::Flags(advgetopt::all_flags<
    2340              :                       advgetopt::GETOPT_FLAG_REQUIRED
    2341              :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2342              :             ),
    2343              :             advgetopt::define_option(
    2344              :                   advgetopt::Name("error::background")
    2345              :                 , advgetopt::Flags(advgetopt::all_flags<
    2346              :                       advgetopt::GETOPT_FLAG_REQUIRED
    2347              :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2348              :             ),
    2349              :             advgetopt::define_option(
    2350              :                   advgetopt::Name("error::foreground")
    2351              :                 , advgetopt::Flags(advgetopt::all_flags<
    2352              :                       advgetopt::GETOPT_FLAG_REQUIRED
    2353              :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2354              :             ),
    2355              :             advgetopt::define_option(
    2356              :                   advgetopt::Name("see-config")
    2357              :                 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
    2358              :             ),
    2359              :             advgetopt::end_options()
    2360              :         };
    2361              : 
    2362            1 :         char const * const configuration_files[] =
    2363              :         {
    2364            1 :             SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(),
    2365              :             nullptr
    2366            1 :         };
    2367              : 
    2368            1 :         advgetopt::options_environment environment_options;
    2369            1 :         environment_options.f_project_name = "unittest";
    2370            1 :         environment_options.f_options = options;
    2371            1 :         environment_options.f_help_header = "Usage: configuration with variables through environment.";
    2372            1 :         environment_options.f_section_variables_name = "variables";
    2373            1 :         environment_options.f_configuration_files = configuration_files;
    2374              : 
    2375            1 :         char const * cargv[] =
    2376              :         {
    2377              :             "/usr/bin/cmd-n-config",
    2378              :             "--see-config",
    2379              :             "--error::foreground",
    2380              :             "${orange}",
    2381              :             nullptr
    2382              :         };
    2383            1 :         int const argc(sizeof(cargv) / sizeof(cargv[0]) - 1);
    2384            1 :         char ** argv = const_cast<char **>(cargv);
    2385              : 
    2386            1 :         advgetopt::getopt::pointer_t opts(std::make_shared<advgetopt::getopt>(environment_options, argc, argv));
    2387            1 :         CATCH_REQUIRE(opts != nullptr);
    2388              : 
    2389            1 :         advgetopt::variables::pointer_t variables(opts->get_variables());
    2390            1 :         CATCH_REQUIRE(variables != nullptr);
    2391              : 
    2392            3 :         CATCH_REQUIRE(opts->is_defined("see-config"));
    2393            3 :         CATCH_REQUIRE(opts->is_defined("edge"));
    2394            3 :         CATCH_REQUIRE(opts->is_defined("background"));
    2395            3 :         CATCH_REQUIRE(opts->is_defined("foreground"));
    2396            3 :         CATCH_REQUIRE(opts->is_defined("error::edge"));
    2397            3 :         CATCH_REQUIRE(opts->is_defined("error::background"));
    2398            3 :         CATCH_REQUIRE(opts->is_defined("error::foreground"));
    2399            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::red"));
    2400            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::green"));
    2401            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::blue"));
    2402            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::no_color"));
    2403            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::black"));
    2404            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::orange"));
    2405            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::white"));
    2406            3 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::gray"));
    2407              : 
    2408            3 :         CATCH_REQUIRE(opts->get_string("edge") == "#0000ff");
    2409            3 :         CATCH_REQUIRE(opts->get_string("background") == "#ffffff");
    2410            3 :         CATCH_REQUIRE(opts->get_string("foreground") == "#000000");
    2411            3 :         CATCH_REQUIRE(opts->get_string("error::edge") == "#ff0000");
    2412            3 :         CATCH_REQUIRE(opts->get_string("error::background") == "#aaaaaa");
    2413            3 :         CATCH_REQUIRE(opts->get_string("error::foreground") == "#80ff00");
    2414              : 
    2415            3 :         CATCH_REQUIRE(opts->get_option("edge")->get_variables() == variables);
    2416            3 :         CATCH_REQUIRE(opts->get_option("background")->get_variables() == variables);
    2417            3 :         CATCH_REQUIRE(opts->get_option("foreground")->get_variables() == variables);
    2418            3 :         CATCH_REQUIRE(opts->get_option("error::edge")->get_variables() == variables);
    2419            3 :         CATCH_REQUIRE(opts->get_option("error::background")->get_variables() == variables);
    2420            3 :         CATCH_REQUIRE(opts->get_option("error::foreground")->get_variables() == variables);
    2421            1 :     }
    2422            7 :     CATCH_END_SECTION()
    2423            7 : }
    2424              : 
    2425              : 
    2426              : 
    2427              : 
    2428            3 : CATCH_TEST_CASE("save_config_file", "[config][getopt][valid]")
    2429              : {
    2430            3 :     CATCH_START_SECTION("save_config_file: load update save (=)")
    2431              :     {
    2432            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("save-operation", "configuration-equal");
    2433              : 
    2434              :         {
    2435            1 :             std::ofstream config_file;
    2436            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2437            1 :             CATCH_REQUIRE(config_file.good());
    2438              :             config_file <<
    2439              :                 "# Auto-generated\n"
    2440              :                 "a=color\n"
    2441              :                 "b=red\n"
    2442            1 :                 "call-flag=122\n"
    2443              :             ;
    2444            1 :         }
    2445              : 
    2446            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2447              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2448              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2449              :                             , advgetopt::COMMENT_SHELL
    2450            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    2451              : 
    2452            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2453              : 
    2454            1 :         CATCH_REQUIRE(setup.is_valid());
    2455            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2456            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2457            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2458            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2459              : 
    2460            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2461              : 
    2462            1 :         CATCH_REQUIRE(file->exists());
    2463            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2464            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2465              : 
    2466            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2467            1 :         CATCH_REQUIRE(sections.empty());
    2468              : 
    2469            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    2470              : 
    2471            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    2472            3 :         CATCH_REQUIRE(file->has_parameter("b"));
    2473            3 :         CATCH_REQUIRE(file->has_parameter("call-flag"));
    2474              : 
    2475            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2476            3 :         CATCH_REQUIRE(file->get_parameter("b") == "red");
    2477            3 :         CATCH_REQUIRE(file->get_parameter("call-flag") == "122");
    2478              : 
    2479            3 :         CATCH_REQUIRE(file->save_configuration());
    2480              : 
    2481              :         // no backup since there was no modification so the save did nothing
    2482              :         //
    2483            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) != 0);
    2484              : 
    2485            5 :         file->set_parameter(std::string(), "a", "size");
    2486            5 :         file->set_parameter(std::string(), "b", "tall");
    2487            5 :         file->set_parameter(std::string(), "call-flag", "1920");
    2488              : 
    2489            3 :         CATCH_REQUIRE(file->save_configuration());
    2490              : 
    2491            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) == 0);
    2492              : 
    2493            5 :         file->set_parameter(std::string(), "a", "pace");
    2494            5 :         file->set_parameter(std::string(), "b", "fall");
    2495            5 :         file->set_parameter(std::string(), "call-flag", "2019");
    2496              : 
    2497            3 :         CATCH_REQUIRE(file->save_configuration("save"));
    2498              : 
    2499            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".save").c_str(), F_OK) == 0);
    2500              : 
    2501            1 :         std::string const new_name(SNAP_CATCH2_NAMESPACE::g_config_filename + ".conf2");
    2502            1 :         rename(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), new_name.c_str());
    2503              : 
    2504            1 :         advgetopt::conf_file_setup setup2(new_name
    2505              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2506              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2507              :                             , advgetopt::COMMENT_SHELL
    2508            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    2509              : 
    2510            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2511              : 
    2512            1 :         CATCH_REQUIRE(setup2.is_valid());
    2513            1 :         CATCH_REQUIRE(setup2.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2514            1 :         CATCH_REQUIRE(setup2.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2515            1 :         CATCH_REQUIRE(setup2.get_comment() == advgetopt::COMMENT_SHELL);
    2516            1 :         CATCH_REQUIRE(setup2.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2517              : 
    2518            1 :         advgetopt::conf_file::pointer_t file2(advgetopt::conf_file::get_conf_file(setup2));
    2519              : 
    2520            1 :         CATCH_REQUIRE(file2->exists());
    2521            1 :         CATCH_REQUIRE(file2->get_setup().get_config_url() == setup2.get_config_url());
    2522            1 :         CATCH_REQUIRE(file2->get_errno() == 0);
    2523              : 
    2524            1 :         CATCH_REQUIRE(file->get_sections().empty());
    2525              : 
    2526            1 :         CATCH_REQUIRE(file2->get_parameters().size() == 3);
    2527              : 
    2528            3 :         CATCH_REQUIRE(file2->has_parameter("a"));
    2529            3 :         CATCH_REQUIRE(file2->has_parameter("b"));
    2530            3 :         CATCH_REQUIRE(file2->has_parameter("call-flag"));
    2531              : 
    2532            3 :         CATCH_REQUIRE(file2->get_parameter("a") == "pace");
    2533            3 :         CATCH_REQUIRE(file2->get_parameter("b") == "fall");
    2534            3 :         CATCH_REQUIRE(file2->get_parameter("call-flag") == "2019");
    2535            1 :     }
    2536            3 :     CATCH_END_SECTION()
    2537              : 
    2538            3 :     CATCH_START_SECTION("save_config_file: load update save (:)")
    2539              :     {
    2540            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("save-operation", "configuration-colon");
    2541              : 
    2542              :         {
    2543            1 :             std::ofstream config_file;
    2544            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2545            1 :             CATCH_REQUIRE(config_file.good());
    2546              :             config_file <<
    2547              :                 "# Auto-generated\n"
    2548              :                 "a: color\n"
    2549              :                 "b: red\n"
    2550            1 :                 "call-flag: 122\n"
    2551              :             ;
    2552            1 :         }
    2553              : 
    2554            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2555              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2556              :                             , advgetopt::ASSIGNMENT_OPERATOR_COLON
    2557              :                             , advgetopt::COMMENT_SHELL
    2558              :                             , advgetopt::SECTION_OPERATOR_NONE
    2559            1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2560              : 
    2561            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2562              : 
    2563            1 :         CATCH_REQUIRE(setup.is_valid());
    2564            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2565            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
    2566            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2567            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2568              : 
    2569            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2570              : 
    2571            1 :         CATCH_REQUIRE(file->exists());
    2572            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2573            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2574              : 
    2575            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2576            1 :         CATCH_REQUIRE(sections.empty());
    2577              : 
    2578            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    2579              : 
    2580            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    2581            3 :         CATCH_REQUIRE(file->has_parameter("b"));
    2582            3 :         CATCH_REQUIRE(file->has_parameter("call-flag"));
    2583              : 
    2584            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2585            3 :         CATCH_REQUIRE(file->get_parameter("b") == "red");
    2586            3 :         CATCH_REQUIRE(file->get_parameter("call-flag") == "122");
    2587              : 
    2588            3 :         CATCH_REQUIRE(file->save_configuration());
    2589              : 
    2590              :         // no backup since there was no modification so the save did nothing
    2591              :         //
    2592            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) != 0);
    2593              : 
    2594            5 :         file->set_parameter(std::string(), "a", "size");
    2595            5 :         file->set_parameter(std::string(), "b", "tall");
    2596            5 :         file->set_parameter(std::string(), "call-flag", "1920");
    2597              : 
    2598            3 :         CATCH_REQUIRE(file->save_configuration());
    2599              : 
    2600            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) == 0);
    2601              : 
    2602            5 :         file->set_parameter(std::string(), "a", "pace");
    2603            5 :         file->set_parameter(std::string(), "b", "fall");
    2604            5 :         file->set_parameter(std::string(), "call-flag", "2019");
    2605              : 
    2606            3 :         CATCH_REQUIRE(file->save_configuration("save"));
    2607              : 
    2608            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".save").c_str(), F_OK) == 0);
    2609              : 
    2610            1 :         std::string const new_name(SNAP_CATCH2_NAMESPACE::g_config_filename + ".conf2");
    2611            1 :         rename(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), new_name.c_str());
    2612              : 
    2613            1 :         advgetopt::conf_file_setup setup2(new_name
    2614              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2615              :                             , advgetopt::ASSIGNMENT_OPERATOR_COLON
    2616              :                             , advgetopt::COMMENT_SHELL
    2617              :                             , advgetopt::SECTION_OPERATOR_NONE
    2618            1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2619              : 
    2620            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2621              : 
    2622            1 :         CATCH_REQUIRE(setup2.is_valid());
    2623            1 :         CATCH_REQUIRE(setup2.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2624            1 :         CATCH_REQUIRE(setup2.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
    2625            1 :         CATCH_REQUIRE(setup2.get_comment() == advgetopt::COMMENT_SHELL);
    2626            1 :         CATCH_REQUIRE(setup2.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2627              : 
    2628            1 :         advgetopt::conf_file::pointer_t file2(advgetopt::conf_file::get_conf_file(setup2));
    2629              : 
    2630            1 :         CATCH_REQUIRE(file2->exists());
    2631            1 :         CATCH_REQUIRE(file2->get_setup().get_config_url() == setup2.get_config_url());
    2632            1 :         CATCH_REQUIRE(file2->get_errno() == 0);
    2633              : 
    2634            1 :         CATCH_REQUIRE(file->get_sections().empty());
    2635              : 
    2636            1 :         CATCH_REQUIRE(file2->get_parameters().size() == 3);
    2637              : 
    2638            3 :         CATCH_REQUIRE(file2->has_parameter("a"));
    2639            3 :         CATCH_REQUIRE(file2->has_parameter("b"));
    2640            3 :         CATCH_REQUIRE(file2->has_parameter("call-flag"));
    2641              : 
    2642            3 :         CATCH_REQUIRE(file2->get_parameter("a") == "pace");
    2643            3 :         CATCH_REQUIRE(file2->get_parameter("b") == "fall");
    2644            3 :         CATCH_REQUIRE(file2->get_parameter("call-flag") == "2019");
    2645            1 :     }
    2646            3 :     CATCH_END_SECTION()
    2647              : 
    2648            3 :     CATCH_START_SECTION("save_config_file: load update save ( )")
    2649              :     {
    2650            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("save-operation", "configuration-space");
    2651              : 
    2652              :         {
    2653            1 :             std::ofstream config_file;
    2654            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2655            1 :             CATCH_REQUIRE(config_file.good());
    2656              :             config_file <<
    2657              :                 "# This comment is kept along the a= variable\n"
    2658              :                 "a color\n"
    2659              :                 "b red\n"
    2660            1 :                 "call-flag 122\n"
    2661              :             ;
    2662            1 :         }
    2663              : 
    2664            1 :         advgetopt::comment_t const comment(advgetopt::COMMENT_SHELL | advgetopt::COMMENT_SAVE);
    2665            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2666              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2667              :                             , advgetopt::ASSIGNMENT_OPERATOR_SPACE
    2668              :                             , comment
    2669              :                             , advgetopt::SECTION_OPERATOR_NONE
    2670            1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2671              : 
    2672            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2673              : 
    2674            1 :         CATCH_REQUIRE(setup.is_valid());
    2675            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2676            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_SPACE);
    2677            1 :         CATCH_REQUIRE(setup.get_comment() == comment);
    2678            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2679              : 
    2680            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2681              : 
    2682            1 :         CATCH_REQUIRE(file->exists());
    2683            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2684            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2685              : 
    2686            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2687            1 :         CATCH_REQUIRE(sections.empty());
    2688              : 
    2689            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    2690              : 
    2691            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    2692            3 :         CATCH_REQUIRE(file->has_parameter("b"));
    2693            3 :         CATCH_REQUIRE(file->has_parameter("call-flag"));
    2694              : 
    2695            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2696            3 :         CATCH_REQUIRE(file->get_parameter("b") == "red");
    2697            3 :         CATCH_REQUIRE(file->get_parameter("call-flag") == "122");
    2698              : 
    2699            1 :         advgetopt::conf_file::parameters_t params(file->get_parameters());
    2700            3 :         auto it(params.find("a"));
    2701            1 :         CATCH_REQUIRE(it != params.end());
    2702            1 :         CATCH_REQUIRE(it->second.get_comment() == "# This comment is kept along the a= variable\n");
    2703            1 :         CATCH_REQUIRE(it->second.get_comment(true) == "# This comment is kept along the a= variable\n");
    2704              : 
    2705            3 :         it->second.set_comment("# Changing the comment");
    2706            1 :         CATCH_REQUIRE(it->second.get_comment() == "# Changing the comment");
    2707            1 :         CATCH_REQUIRE(it->second.get_comment(true) == "# Changing the comment\n");
    2708              : 
    2709            3 :         CATCH_REQUIRE(file->save_configuration());
    2710              : 
    2711              :         // no backup since there was no modification so the save did nothing
    2712              :         //
    2713            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) != 0);
    2714              : 
    2715            5 :         file->set_parameter(std::string(), "a", "size");
    2716            5 :         file->set_parameter(std::string(), "b", "tall");
    2717            5 :         file->set_parameter(std::string(), "call-flag", "1920");
    2718              : 
    2719            3 :         CATCH_REQUIRE(file->save_configuration());
    2720              : 
    2721            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) == 0);
    2722              : 
    2723            5 :         file->set_parameter(std::string(), "a", "pace");
    2724            5 :         file->set_parameter(std::string(), "b", "fall");
    2725            5 :         file->set_parameter(std::string(), "call-flag", "2019");
    2726              : 
    2727            3 :         it->second.set_value("warning"); // WARNING: the parameters_t is a copy so you can't change the parameters that way
    2728            1 :         CATCH_REQUIRE(it->second.get_value() == "warning");
    2729            3 :         CATCH_REQUIRE(file->get_parameter("a") == "pace");
    2730              : 
    2731              :         // the following constructor and assignment are defined although not
    2732              :         // used within the library at the moment
    2733              :         //
    2734            3 :         advgetopt::parameter_value const value("other value");
    2735            1 :         it->second = value;
    2736            1 :         CATCH_REQUIRE(it->second.get_value() == "other value");
    2737            3 :         CATCH_REQUIRE(file->get_parameter("a") == "pace");
    2738              : 
    2739            3 :         CATCH_REQUIRE(file->save_configuration("save"));
    2740              : 
    2741            1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".save").c_str(), F_OK) == 0);
    2742              : 
    2743            1 :         std::string const new_name(SNAP_CATCH2_NAMESPACE::g_config_filename + ".conf2");
    2744            1 :         rename(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), new_name.c_str());
    2745              : 
    2746            1 :         advgetopt::conf_file_setup setup2(new_name
    2747              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2748              :                             , advgetopt::ASSIGNMENT_OPERATOR_SPACE
    2749              :                             , comment
    2750              :                             , advgetopt::SECTION_OPERATOR_NONE
    2751            1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2752              : 
    2753            1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2754              : 
    2755            1 :         CATCH_REQUIRE(setup2.is_valid());
    2756            1 :         CATCH_REQUIRE(setup2.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2757            1 :         CATCH_REQUIRE(setup2.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_SPACE);
    2758            1 :         CATCH_REQUIRE(setup2.get_comment() == comment);
    2759            1 :         CATCH_REQUIRE(setup2.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2760              : 
    2761            1 :         advgetopt::conf_file::pointer_t file2(advgetopt::conf_file::get_conf_file(setup2));
    2762              : 
    2763            1 :         CATCH_REQUIRE(file2->exists());
    2764            1 :         CATCH_REQUIRE(file2->get_setup().get_config_url() == setup2.get_config_url());
    2765            1 :         CATCH_REQUIRE(file2->get_errno() == 0);
    2766              : 
    2767            1 :         CATCH_REQUIRE(file->get_sections().empty());
    2768              : 
    2769            1 :         CATCH_REQUIRE(file2->get_parameters().size() == 3);
    2770              : 
    2771            3 :         CATCH_REQUIRE(file2->has_parameter("a"));
    2772            3 :         CATCH_REQUIRE(file2->has_parameter("b"));
    2773            3 :         CATCH_REQUIRE(file2->has_parameter("call-flag"));
    2774              : 
    2775            3 :         CATCH_REQUIRE(file2->get_parameter("a") == "pace");
    2776            3 :         CATCH_REQUIRE(file2->get_parameter("b") == "fall");
    2777            3 :         CATCH_REQUIRE(file2->get_parameter("call-flag") == "2019");
    2778              : 
    2779            1 :         file2->erase_all_parameters();
    2780            3 :         CATCH_REQUIRE_FALSE(file2->has_parameter("a"));
    2781            3 :         CATCH_REQUIRE_FALSE(file2->has_parameter("b"));
    2782            3 :         CATCH_REQUIRE_FALSE(file2->has_parameter("call-flag"));
    2783            1 :     }
    2784            3 :     CATCH_END_SECTION()
    2785            3 : }
    2786              : 
    2787              : 
    2788              : 
    2789              : 
    2790              : 
    2791              : 
    2792              : 
    2793            2 : CATCH_TEST_CASE("invalid_configuration_setup", "[config][getopt][invalid]")
    2794              : {
    2795            2 :     CATCH_START_SECTION("invalid_configuration_setup: empty filename")
    2796              :     {
    2797            6 :         CATCH_REQUIRE_THROWS_MATCHES(
    2798              :               advgetopt::conf_file_setup(
    2799              :                               std::string()
    2800              :                             , static_cast<advgetopt::line_continuation_t>(rand())
    2801              :                             , rand()
    2802              :                             , rand()
    2803              :                             , rand())
    2804              :             , advgetopt::getopt_invalid
    2805              :             , Catch::Matchers::ExceptionMessage(
    2806              :                           "getopt_exception: trying to load a configuration file using an empty filename."));
    2807              :     }
    2808            2 :     CATCH_END_SECTION()
    2809              : 
    2810            2 :     CATCH_START_SECTION("invalid_configuration_setup: invalid line continuation")
    2811              :     {
    2812            6 :         for(int count(0); count < 5; ++count)
    2813              :         {
    2814            5 :             advgetopt::line_continuation_t lc(advgetopt::line_continuation_t::line_continuation_unix);
    2815              :             do
    2816              :             {
    2817            5 :                 lc = static_cast<advgetopt::line_continuation_t>(rand());
    2818              :             }
    2819              :             while(lc >= advgetopt::line_continuation_t::line_continuation_single_line
    2820            5 :                && lc <= advgetopt::line_continuation_t::line_continuation_semicolon);
    2821              : 
    2822            5 :             advgetopt::conf_file_setup setup(
    2823              :                           "/etc/advgetopt/system.conf"
    2824              :                         , lc        // <- this is invalid
    2825           10 :                         , rand() & advgetopt::ASSIGNMENT_OPERATOR_MASK
    2826           10 :                         , rand() & advgetopt::COMMENT_MASK
    2827           15 :                         , rand() & advgetopt::SECTION_OPERATOR_MASK);
    2828              : 
    2829            5 :             CATCH_REQUIRE(setup.is_valid());
    2830              : 
    2831           20 :             CATCH_REQUIRE_THROWS_MATCHES(
    2832              :                   setup.get_config_url()
    2833              :                 , advgetopt::getopt_logic_error
    2834              :                 , Catch::Matchers::ExceptionMessage(
    2835              :                               "getopt_logic_error: unexpected line continuation."));
    2836            5 :         }
    2837              :     }
    2838            2 :     CATCH_END_SECTION()
    2839            2 : }
    2840              : 
    2841              : 
    2842              : 
    2843              : 
    2844              : 
    2845              : 
    2846              : 
    2847            1 : CATCH_TEST_CASE("config_reload_invalid_setup", "[config][getopt][invalid]")
    2848              : {
    2849            1 :     CATCH_START_SECTION("config_reload_invalid_setup: load a file, update it, verify it does not get reloaded")
    2850              :     {
    2851            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-reload", "load-twice-wrong-parameters");
    2852              : 
    2853              :         {
    2854            1 :             std::ofstream config_file;
    2855            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2856            1 :             CATCH_REQUIRE(config_file.good());
    2857              :             config_file <<
    2858              :                 "# Auto-generated\n"
    2859              :                 "duplicates=work\n"
    2860              :                 "varying=parameters\n"
    2861              :                 "however=is\n"
    2862            1 :                 "not=valid\n"
    2863              :             ;
    2864            1 :         }
    2865              : 
    2866            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2867              :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2868              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2869              :                             , advgetopt::COMMENT_SHELL
    2870            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    2871              : 
    2872            1 :         CATCH_REQUIRE(setup.is_valid());
    2873            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2874            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2875            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2876            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2877              : 
    2878            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2879              : 
    2880            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2881            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2882            1 :         CATCH_REQUIRE(file->get_sections().empty());
    2883            1 :         CATCH_REQUIRE(file->get_parameters().size() == 4);
    2884              : 
    2885            3 :         CATCH_REQUIRE(file->has_parameter("duplicates"));
    2886            3 :         CATCH_REQUIRE(file->has_parameter("varying"));
    2887            3 :         CATCH_REQUIRE(file->has_parameter("however"));
    2888            3 :         CATCH_REQUIRE(file->has_parameter("not"));
    2889              : 
    2890            3 :         CATCH_REQUIRE(file->get_parameter("duplicates") == "work");
    2891            3 :         CATCH_REQUIRE(file->get_parameter("varying") == "parameters");
    2892            3 :         CATCH_REQUIRE(file->get_parameter("however") == "is");
    2893            3 :         CATCH_REQUIRE(file->get_parameter("not") == "valid");
    2894              : 
    2895              :         // "reloading" that very same file but with the "wrong" parameters
    2896              :         // fails
    2897              :         //
    2898            7 :         for(int lc(static_cast<int>(advgetopt::line_continuation_t::line_continuation_single_line));
    2899            7 :             lc <= static_cast<int>(advgetopt::line_continuation_t::line_continuation_semicolon);
    2900              :             ++lc)
    2901              :         {
    2902            6 :             if(static_cast<advgetopt::line_continuation_t>(lc) == advgetopt::line_continuation_t::line_continuation_single_line)
    2903              :             {
    2904            1 :                 continue;
    2905              :             }
    2906              : 
    2907           85 :             for(advgetopt::assignment_operator_t ao(0);
    2908           85 :                 ao <= advgetopt::ASSIGNMENT_OPERATOR_MASK;
    2909              :                 ++ao)
    2910              :             {
    2911           80 :                 if(ao == advgetopt::ASSIGNMENT_OPERATOR_EQUAL)
    2912              :                 {
    2913            5 :                     continue;
    2914              :                 }
    2915              : 
    2916          600 :                 for(advgetopt::comment_t c(0);
    2917          600 :                     c < advgetopt::COMMENT_MASK;
    2918              :                     ++c)
    2919              :                 {
    2920          525 :                     if(c == advgetopt::COMMENT_SHELL)
    2921              :                     {
    2922           75 :                         continue;
    2923              :                     }
    2924              : 
    2925         7200 :                     for(advgetopt::section_operator_t so(0);
    2926         7200 :                         so < advgetopt::SECTION_OPERATOR_MASK;
    2927              :                         ++so)
    2928              :                     {
    2929         6750 :                         if(c == advgetopt::SECTION_OPERATOR_NONE)
    2930              :                         {
    2931         1125 :                             continue;
    2932              :                         }
    2933              : 
    2934         5625 :                         advgetopt::conf_file_setup different_setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2935              :                                         , static_cast<advgetopt::line_continuation_t>(lc)
    2936              :                                         , ao
    2937              :                                         , c
    2938         5625 :                                         , so);
    2939              : 
    2940        11250 :                         CATCH_REQUIRE_THROWS_MATCHES(
    2941              :                               advgetopt::conf_file::get_conf_file(different_setup)
    2942              :                             , advgetopt::getopt_logic_error
    2943              :                             , Catch::Matchers::ExceptionMessage(
    2944              :                                           "getopt_logic_error: trying to load configuration file \""
    2945              :                                         + different_setup.get_config_url()
    2946              :                                         + "\" but an existing configuration file with the same name was loaded with URL: \""
    2947              :                                         + setup.get_config_url()
    2948              :                                         + "\"."));
    2949         5625 :                     }
    2950              :                 }
    2951              :             }
    2952              :         }
    2953            1 :     }
    2954            1 :     CATCH_END_SECTION()
    2955            1 : }
    2956              : 
    2957              : 
    2958            1 : CATCH_TEST_CASE("missing_configuration_file", "[config][getopt][invalid]")
    2959              : {
    2960            1 :     CATCH_START_SECTION("missing_configuration_file: create a conf_file without the file")
    2961              :     {
    2962            6 :         for(int count(0); count < 5; ++count)
    2963              :         {
    2964            5 :             int const id(rand());
    2965            5 :             std::string const name("delete-file-" + std::to_string(id));
    2966              : 
    2967           15 :             SNAP_CATCH2_NAMESPACE::init_tmp_dir("delete", name);
    2968              : 
    2969              :             {
    2970            5 :                 std::ofstream config_file;
    2971            5 :                 config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2972            5 :                 CATCH_REQUIRE(config_file.good());
    2973              :                 config_file <<
    2974              :                     "# Auto-generated\n"
    2975            5 :                     "param=optional\n"
    2976              :                 ;
    2977            5 :             }
    2978              : 
    2979              :             // create the setup while the file still exists
    2980              :             //
    2981            5 :             advgetopt::conf_file_setup setup(
    2982              :                           SNAP_CATCH2_NAMESPACE::g_config_filename
    2983              :                         , advgetopt::line_continuation_t::line_continuation_unix
    2984              :                         , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2985              :                         , advgetopt::COMMENT_SHELL
    2986            5 :                         , advgetopt::SECTION_OPERATOR_NONE);
    2987              : 
    2988              :             // get the full name before the unlink()
    2989            5 :             std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    2990              : 
    2991              :             // now unlink() that file
    2992              :             //
    2993            5 :             unlink(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str());
    2994              : 
    2995              :             // still valid since we do not check again after the
    2996              :             // constructor ran
    2997              :             //
    2998            5 :             CATCH_REQUIRE(setup.is_valid());
    2999            5 :             CATCH_REQUIRE(setup.get_filename() == fn.get());
    3000            5 :             CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3001            5 :             CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3002            5 :             CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3003            5 :             CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    3004              : 
    3005              :             // so when trying to create the conf_file object it fails
    3006              :             // opening the file
    3007              :             //
    3008            5 :             advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3009            5 :             CATCH_REQUIRE(file->get_errno() == ENOENT);
    3010            5 :             CATCH_REQUIRE_FALSE(file->exists());
    3011            5 :         }
    3012              :     }
    3013            1 :     CATCH_END_SECTION()
    3014            1 : }
    3015              : 
    3016              : 
    3017            9 : CATCH_TEST_CASE("invalid_sections", "[config][getopt][invalid]")
    3018              : {
    3019            9 :     CATCH_START_SECTION("invalid_sections: variable name cannot start with a period when C operator is active")
    3020              :     {
    3021            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "period-name");
    3022              : 
    3023              :         {
    3024            1 :             std::ofstream config_file;
    3025            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3026            1 :             CATCH_REQUIRE(config_file.good());
    3027              :             config_file <<
    3028              :                 "# Auto-generated\n"
    3029              :                 "a=color\n"
    3030              :                 "a..b=red\n"
    3031              :                 ".a.b.c=122\n"
    3032              :                 "m=size\n"
    3033              :                 "z=edge\n"
    3034              :                 "z.b=line\n"
    3035            1 :                 "z..b.c=12.72\n"
    3036              :             ;
    3037            1 :         }
    3038              : 
    3039            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3040              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3041              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3042              :                             , advgetopt::COMMENT_SHELL
    3043            1 :                             , advgetopt::SECTION_OPERATOR_C);
    3044              : 
    3045            1 :         CATCH_REQUIRE(setup.is_valid());
    3046            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3047            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3048            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3049            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3050              : 
    3051            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3052              :                     "error: option name \".a.b.c\" cannot start with"
    3053              :                     " a period (.).");
    3054            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3055            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3056              : 
    3057            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3058            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3059              : 
    3060            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3061            1 :         CATCH_REQUIRE(sections.size() == 3);
    3062            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3063            3 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    3064            3 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    3065              : 
    3066            1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3067              : 
    3068            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    3069            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3070            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    3071            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    3072            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    3073            3 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    3074              : 
    3075            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3076            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3077            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    3078            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3079            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    3080            3 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    3081            1 :     }
    3082            9 :     CATCH_END_SECTION()
    3083              : 
    3084            9 :     CATCH_START_SECTION("invalid_sections: two section operators one after another can cause trouble")
    3085              :     {
    3086            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "name-period-cpp-name");
    3087              : 
    3088              :         {
    3089            1 :             std::ofstream config_file;
    3090            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3091            1 :             CATCH_REQUIRE(config_file.good());
    3092              :             config_file <<
    3093              :                 "# Auto-generated\n"
    3094              :                 "a=color\n"
    3095              :                 "a..b=red\n"
    3096              :                 "a.::b.c=122\n"
    3097              :                 "m=size\n"
    3098              :                 "z=edge\n"
    3099              :                 "z.b=line\n"
    3100            1 :                 "z..b.c=12.72\n"
    3101              :             ;
    3102            1 :         }
    3103              : 
    3104            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3105              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3106              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3107              :                             , advgetopt::COMMENT_SHELL
    3108            1 :                             , advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP);
    3109              : 
    3110            1 :         CATCH_REQUIRE(setup.is_valid());
    3111            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3112            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3113            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3114            1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP));
    3115              : 
    3116            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3117              :                     "error: option name \"a.::b.c\" cannot start with"
    3118              :                     " a scope operator (::).");
    3119            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3120            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3121              : 
    3122            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3123            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3124              : 
    3125            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3126            1 :         CATCH_REQUIRE(sections.size() == 3);
    3127            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3128            3 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    3129            3 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    3130              : 
    3131            1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3132              : 
    3133            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    3134            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3135            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    3136            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    3137            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    3138            3 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    3139              : 
    3140            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3141            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3142            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    3143            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3144            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    3145            3 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    3146            1 :     }
    3147            9 :     CATCH_END_SECTION()
    3148              : 
    3149            9 :     CATCH_START_SECTION("invalid_sections: section operator cannot appear at the end")
    3150              :     {
    3151            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "name-period-name-cpp");
    3152              : 
    3153              :         {
    3154            1 :             std::ofstream config_file;
    3155            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3156            1 :             CATCH_REQUIRE(config_file.good());
    3157              :             config_file <<
    3158              :                 "# Auto-generated\n"
    3159              :                 "a=color\n"
    3160              :                 "a..b=red\n"
    3161              :                 "a.b.c::=122\n"
    3162              :                 "m=size\n"
    3163              :                 "z=edge\n"
    3164              :                 "z.b=line\n"
    3165            1 :                 "z..b.c=12.72\n"
    3166              :             ;
    3167            1 :         }
    3168              : 
    3169            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3170              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3171              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3172              :                             , advgetopt::COMMENT_SHELL
    3173            1 :                             , advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP);
    3174              : 
    3175            1 :         CATCH_REQUIRE(setup.is_valid());
    3176            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3177            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3178            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3179            1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP));
    3180              : 
    3181            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3182              :                     "error: option name \"a.b.c::\" cannot end with a"
    3183              :                     " section operator or be empty.");
    3184            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3185            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3186              : 
    3187            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3188            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3189              : 
    3190            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3191            1 :         CATCH_REQUIRE(sections.size() == 3);
    3192            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3193            3 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    3194            3 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    3195              : 
    3196            1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3197              : 
    3198            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    3199            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3200            3 :         CATCH_REQUIRE(file->has_parameter("m"));
    3201            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    3202            3 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    3203            3 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    3204              : 
    3205            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3206            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3207            3 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    3208            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3209            3 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    3210            3 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    3211            1 :     }
    3212            9 :     CATCH_END_SECTION()
    3213              : 
    3214            9 :     CATCH_START_SECTION("invalid_sections: sections not allowed")
    3215              :     {
    3216            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "section-not-allowed");
    3217              : 
    3218              :         {
    3219            1 :             std::ofstream config_file;
    3220            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3221            1 :             CATCH_REQUIRE(config_file.good());
    3222              :             config_file <<
    3223              :                 "# Auto-generated\n"
    3224              :                 "a=color\n"
    3225              :                 "a::b=red\n"
    3226              :                 "m.n=size\n"
    3227            1 :                 "z=edge\n"
    3228              :             ;
    3229            1 :         }
    3230              : 
    3231              :         // no errors here since we do not detect the sections in this case
    3232              :         //
    3233            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3234              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3235              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3236              :                             , advgetopt::COMMENT_SHELL
    3237            1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    3238              : 
    3239            1 :         CATCH_REQUIRE(setup.is_valid());
    3240            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3241            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3242            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3243            1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_NONE));
    3244              : 
    3245            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3246            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3247              :                   "error: section \"a::b\" from parameter \"a::b\" on line 3 in configuration file \""
    3248            4 :                 + std::string(fn.get())
    3249            4 :                 + "\" includes a character (\\072) not acceptable for a section or"
    3250              :                   " parameter name (controls, space, quotes, and \";#/=:?+\\\").");
    3251            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3252            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3253              : 
    3254            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3255            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3256              : 
    3257            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3258            1 :         CATCH_REQUIRE(sections.empty());
    3259              : 
    3260            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    3261              : 
    3262            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    3263            3 :         CATCH_REQUIRE_FALSE(file->has_parameter("a::b"));
    3264            3 :         CATCH_REQUIRE(file->has_parameter("m.n"));
    3265            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    3266              : 
    3267            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3268            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == std::string());
    3269            3 :         CATCH_REQUIRE(file->get_parameter("m.n") == "size");
    3270            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3271              : 
    3272            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3273              :                     "error: option name \"blue::shepard\" cannot be added to"
    3274              :                     " section \"j::k\" because there is no section support"
    3275              :                     " for this configuration file.");
    3276            7 :         file->set_parameter("j::k", "blue::shepard", "2001");
    3277            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3278            1 :     }
    3279            9 :     CATCH_END_SECTION()
    3280              : 
    3281            9 :     CATCH_START_SECTION("invalid_sections: invalid characters in names")
    3282              :     {
    3283            1 :         std::string const bad_chars(
    3284              :                     "\x01\x02\x03\x04\x05\x06\x07"
    3285              :                 "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
    3286              :                 "\x10\x11\x12\x13\x14\x15\x16\x17"
    3287              :                 "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
    3288              :                 "\x20"
    3289              :                 "'\";#/=:?+\\"
    3290            3 :             );
    3291           43 :         for(auto c : bad_chars)
    3292              :         {
    3293              :             // white spaces get removed from the line so we cannot test
    3294              :             // them in this way
    3295              :             //
    3296           42 :             if(std::iswspace(c))
    3297              :             {
    3298            6 :                 continue;
    3299              :             }
    3300           36 :             std::string bc;
    3301           36 :             bc += c;
    3302              : 
    3303          144 :             for(int pos(0); pos < 3; ++pos)
    3304              :             {
    3305          324 :                 std::string spos("undefined");
    3306          324 :                 std::string bad_char("undefined");
    3307          108 :                 switch(pos)
    3308              :                 {
    3309           36 :                 case 0:
    3310           36 :                     spos = "start";
    3311           36 :                     bad_char = bc + "bad-char";
    3312           36 :                     break;
    3313              : 
    3314           36 :                 case 1:
    3315           36 :                     spos = "middle";
    3316           36 :                     bad_char = "bad" + bc + "char";
    3317           36 :                     break;
    3318              : 
    3319           36 :                 case 2:
    3320           36 :                     spos = "end";
    3321           36 :                     bad_char = "bad-char" + bc;
    3322           36 :                     break;
    3323              : 
    3324              :                 }
    3325          324 :                 SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-characters", "bad-character-" + std::to_string(static_cast<int>(c)) + "-" + spos);
    3326              : 
    3327              :                 {
    3328          108 :                     std::ofstream config_file;
    3329          108 :                     config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3330          108 :                     CATCH_REQUIRE(config_file.good());
    3331          108 :                     char op(c == '=' ? ':' : '=');
    3332              :                     config_file <<
    3333              :                            "good" << op << "red\n"
    3334              :                         << bad_char << op << "color\n"       // <-- bad char
    3335          108 :                            "fine" << op << "param\n";
    3336              :                     ;
    3337          108 :                 }
    3338              : 
    3339              :                 // no errors here since we do not detect the sections in this case
    3340              :                 //
    3341          108 :                 advgetopt::assignment_operator_t as(c == '='
    3342          108 :                                         ? advgetopt::ASSIGNMENT_OPERATOR_COLON
    3343              :                                         : advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3344          108 :                 advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3345              :                                     , advgetopt::line_continuation_t::line_continuation_unix
    3346              :                                     , as
    3347              :                                     , advgetopt::COMMENT_NONE
    3348          216 :                                     , advgetopt::SECTION_OPERATOR_NONE);
    3349              : 
    3350          108 :                 CATCH_REQUIRE(setup.is_valid());
    3351          108 :                 CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3352          108 :                 CATCH_REQUIRE(setup.get_assignment_operator() == as);
    3353          108 :                 CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_NONE);
    3354          108 :                 CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_NONE));
    3355              : 
    3356          216 :                 std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3357          216 :                 std::stringstream octal_char;
    3358          108 :                 octal_char << std::oct << std::setw(3) << std::setfill('0') << static_cast<int>(c);
    3359          216 :                 SNAP_CATCH2_NAMESPACE::push_expected_log(
    3360              :                           "error: section \""
    3361          108 :                         + bad_char
    3362          324 :                         + "\" from parameter \""
    3363          432 :                         + bad_char
    3364          432 :                         + "\" on line 2 in configuration file \""
    3365          432 :                         + fn.get()
    3366          432 :                         + "\" includes a character (\\"
    3367          432 :                         + octal_char.str()
    3368          432 :                         + ") not acceptable for a section or"
    3369              :                           " parameter name (controls, space, quotes, and \";#/=:?+\\\").");
    3370          216 :                 advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3371              : 
    3372          108 :                 CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3373          108 :                 CATCH_REQUIRE(file->get_errno() == 0);
    3374              : 
    3375          216 :                 advgetopt::conf_file::sections_t sections(file->get_sections());
    3376          108 :                 CATCH_REQUIRE(sections.empty());
    3377              : 
    3378          108 :                 CATCH_REQUIRE(file->get_parameters().size() == 2);
    3379              : 
    3380          324 :                 CATCH_REQUIRE(file->has_parameter("good"));
    3381          108 :                 CATCH_REQUIRE_FALSE(file->has_parameter(bad_char));
    3382          324 :                 CATCH_REQUIRE(file->has_parameter("fine"));
    3383              : 
    3384          324 :                 CATCH_REQUIRE(file->get_parameter("good") == "red");
    3385          108 :                 CATCH_REQUIRE(file->get_parameter(bad_char) == std::string());
    3386          324 :                 CATCH_REQUIRE(file->get_parameter("fine") == "param");
    3387          108 :             }
    3388           36 :         }
    3389            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3390            1 :     }
    3391            9 :     CATCH_END_SECTION()
    3392              : 
    3393            9 :     CATCH_START_SECTION("invalid_sections: too many sections")
    3394              :     {
    3395            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "too-many-sections");
    3396              : 
    3397              :         {
    3398            1 :             std::ofstream config_file;
    3399            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3400            1 :             CATCH_REQUIRE(config_file.good());
    3401              :             config_file <<
    3402              :                 "# Auto-generated\n"
    3403              :                 "a=color\n"
    3404              :                 "a::b=red\n"
    3405              :                 "m.n.o=size\n"
    3406            1 :                 "z=edge\n"
    3407              :             ;
    3408            1 :         }
    3409              : 
    3410            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3411              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3412              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3413              :                             , advgetopt::COMMENT_SHELL
    3414            1 :                             , advgetopt::SECTION_OPERATOR_CPP | advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_ONE_SECTION);
    3415              : 
    3416            1 :         CATCH_REQUIRE(setup.is_valid());
    3417            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3418            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3419            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3420            1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_CPP | advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_ONE_SECTION));
    3421              : 
    3422            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3423              :                     "error: option name \"m.n.o\" cannot be added to section"
    3424              :                     " \"m::n\" because this configuration only accepts one"
    3425              :                     " section level.");
    3426            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3427            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3428              : 
    3429            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3430            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3431              : 
    3432            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3433            1 :         CATCH_REQUIRE(sections.size() == 1);
    3434            3 :         CATCH_REQUIRE(sections.find("a") != sections.end());
    3435              : 
    3436            1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    3437              : 
    3438            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    3439            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3440            3 :         CATCH_REQUIRE(file->has_parameter("z"));
    3441              : 
    3442            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3443            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3444            3 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3445            1 :     }
    3446            9 :     CATCH_END_SECTION()
    3447              : 
    3448            9 :     CATCH_START_SECTION("invalid_sections: all '{' were not closed")
    3449              :     {
    3450            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "unclosed-brackets");
    3451              : 
    3452              :         {
    3453            1 :             std::ofstream config_file;
    3454            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3455            1 :             CATCH_REQUIRE(config_file.good());
    3456              :             config_file <<
    3457              :                 "# Auto-generated\n"
    3458              :                 "colors {\n"
    3459              :                 "  b=red\n"
    3460            1 :                 "  c=blue\n"
    3461              :             ;
    3462            1 :         }
    3463              : 
    3464            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3465              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3466              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3467              :                             , advgetopt::COMMENT_SHELL
    3468            1 :                             , advgetopt::SECTION_OPERATOR_BLOCK);
    3469              : 
    3470            1 :         CATCH_REQUIRE(setup.is_valid());
    3471            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3472            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3473            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3474            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_BLOCK);
    3475              : 
    3476            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3477            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3478              :                     "error: unterminated `section { ... }`, the `}` is missing"
    3479              :                     " in configuration file "
    3480              :                     "\""
    3481            4 :                   + std::string(fn.get())
    3482            4 :                   + "\".");
    3483            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3484            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3485              : 
    3486            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3487            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3488              : 
    3489            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3490            1 :         CATCH_REQUIRE(sections.size() == 1);
    3491            3 :         CATCH_REQUIRE(sections.find("colors") != sections.end());
    3492              : 
    3493            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3494              : 
    3495            3 :         CATCH_REQUIRE(file->has_parameter("colors::b"));
    3496            3 :         CATCH_REQUIRE(file->has_parameter("colors::c"));
    3497              : 
    3498            3 :         CATCH_REQUIRE(file->get_parameter("colors::b") == "red");
    3499            3 :         CATCH_REQUIRE(file->get_parameter("colors::c") == "blue");
    3500            1 :     }
    3501            9 :     CATCH_END_SECTION()
    3502              : 
    3503            9 :     CATCH_START_SECTION("invalid_sections: data after ']' in INI file")
    3504              :     {
    3505            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "additional-data");
    3506              : 
    3507              :         {
    3508            1 :             std::ofstream config_file;
    3509            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3510            1 :             CATCH_REQUIRE(config_file.good());
    3511              :             config_file <<
    3512              :                 "# Auto-generated\n"
    3513              :                 "[colors]\n"
    3514              :                 "b=red\n"
    3515              :                 "c=blue\n"
    3516              :                 "\n"
    3517              :                 "[sizes] comment\n"     // <- missing the comment introducer
    3518              :                 "q=1000\n"
    3519            1 :                 "r=9999\n"
    3520              :             ;
    3521            1 :         }
    3522              : 
    3523            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3524              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3525              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3526              :                             , advgetopt::COMMENT_SHELL
    3527            1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE);
    3528              : 
    3529            1 :         CATCH_REQUIRE(setup.is_valid());
    3530            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3531            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3532            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3533            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_INI_FILE);
    3534              : 
    3535            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3536            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3537              :                       "error: section names in configuration files cannot be followed by anything other than spaces in"
    3538              :                       " \"[sizes] comment\" on line 6 from configuration file \""
    3539            4 :                     + std::string(fn.get())
    3540            4 :                     + "\".");
    3541            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3542            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3543              : 
    3544            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3545            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3546              : 
    3547            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3548            1 :         CATCH_REQUIRE(sections.size() == 1);
    3549            3 :         CATCH_REQUIRE(sections.find("colors") != sections.end());
    3550              : 
    3551            1 :         CATCH_REQUIRE(file->get_parameters().size() == 4);
    3552              : 
    3553            3 :         CATCH_REQUIRE(file->has_parameter("colors::b"));
    3554            3 :         CATCH_REQUIRE(file->has_parameter("colors::c"));
    3555            3 :         CATCH_REQUIRE(file->has_parameter("colors::q"));
    3556            3 :         CATCH_REQUIRE(file->has_parameter("colors::r"));
    3557              : 
    3558            3 :         CATCH_REQUIRE(file->get_parameter("colors::b") == "red");
    3559            3 :         CATCH_REQUIRE(file->get_parameter("colors::c") == "blue");
    3560            3 :         CATCH_REQUIRE(file->get_parameter("colors::q") == "1000");
    3561            3 :         CATCH_REQUIRE(file->get_parameter("colors::r") == "9999");
    3562            1 :     }
    3563            9 :     CATCH_END_SECTION()
    3564              : 
    3565            9 :     CATCH_START_SECTION("invalid_sections: INI file section inside a block is not allowed")
    3566              :     {
    3567            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "ini-inside-block");
    3568              : 
    3569              :         {
    3570            1 :             std::ofstream config_file;
    3571            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3572            1 :             CATCH_REQUIRE(config_file.good());
    3573              :             config_file <<
    3574              :                 "# Auto-generated\n"
    3575              :                 "[colors]\n"
    3576              :                 "b=red\n"
    3577              :                 "c=blue\n"
    3578              :                 "\n"
    3579              :                 "block {\n"
    3580              :                 "  b = block data\n"
    3581              :                 "  f = filename\n"
    3582              :                 "  [sizes]\n"       // <-- INI section inside a block not allowed
    3583              :                 "  q=1000\n"
    3584              :                 "  r=9999\n"
    3585            1 :                 "}\n"
    3586              :             ;
    3587            1 :         }
    3588              : 
    3589            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3590              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3591              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3592              :                             , advgetopt::COMMENT_SHELL
    3593            1 :                             , advgetopt::SECTION_OPERATOR_BLOCK | advgetopt::SECTION_OPERATOR_INI_FILE);
    3594              : 
    3595            1 :         CATCH_REQUIRE(setup.is_valid());
    3596            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3597            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3598            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3599            1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_BLOCK | advgetopt::SECTION_OPERATOR_INI_FILE));
    3600              : 
    3601            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3602            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3603              :                       "error: `[...]` sections can't be used within a `section"
    3604              :                       " { ... }` on line 9 from configuration file \""
    3605            4 :                     + std::string(fn.get())
    3606            4 :                     + "\".");
    3607            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3608            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3609              : 
    3610            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3611            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3612              : 
    3613            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3614            1 :         CATCH_REQUIRE(sections.size() == 2);
    3615            3 :         CATCH_REQUIRE(sections.find("colors") != sections.end());
    3616            3 :         CATCH_REQUIRE(sections.find("colors::block") != sections.end());
    3617              : 
    3618            1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3619              : 
    3620            3 :         CATCH_REQUIRE(file->has_parameter("colors::b"));
    3621            3 :         CATCH_REQUIRE(file->has_parameter("colors::c"));
    3622            3 :         CATCH_REQUIRE(file->has_parameter("colors::block::b"));
    3623            3 :         CATCH_REQUIRE(file->has_parameter("colors::block::f"));
    3624            3 :         CATCH_REQUIRE(file->has_parameter("colors::block::q"));
    3625            3 :         CATCH_REQUIRE(file->has_parameter("colors::block::r"));
    3626              : 
    3627            3 :         CATCH_REQUIRE(file->get_parameter("colors::b") == "red");
    3628            3 :         CATCH_REQUIRE(file->get_parameter("colors::c") == "blue");
    3629            3 :         CATCH_REQUIRE(file->get_parameter("colors::block::b") == "block data");
    3630            3 :         CATCH_REQUIRE(file->get_parameter("colors::block::f") == "filename");
    3631            3 :         CATCH_REQUIRE(file->get_parameter("colors::block::q") == "1000");
    3632            3 :         CATCH_REQUIRE(file->get_parameter("colors::block::r") == "9999");
    3633            1 :     }
    3634            9 :     CATCH_END_SECTION()
    3635            9 : }
    3636              : 
    3637              : 
    3638            5 : CATCH_TEST_CASE("invalid_field_name", "[config][getopt][invalid]")
    3639              : {
    3640            5 :     CATCH_START_SECTION("invalid_field_name: empty field name")
    3641              :     {
    3642            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "name-missing");
    3643              : 
    3644              :         {
    3645            1 :             std::ofstream config_file;
    3646            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3647            1 :             CATCH_REQUIRE(config_file.good());
    3648              :             config_file <<
    3649              :                 "# Auto-generated\n"
    3650              :                 "=color\n"                  // <-- name missing
    3651              :                 "a..b=red\n"
    3652            1 :                 "a.b.c=142\n"
    3653              :             ;
    3654            1 :         }
    3655              : 
    3656            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3657              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3658              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3659              :                             , advgetopt::COMMENT_SHELL
    3660            1 :                             , advgetopt::SECTION_OPERATOR_C);
    3661              : 
    3662            1 :         CATCH_REQUIRE(setup.is_valid());
    3663            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3664            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3665            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3666            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3667              : 
    3668            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3669            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3670              :                       "error: no option name in \"=color\""
    3671              :                       " on line 2 from configuration file \""
    3672            4 :                     + std::string(fn.get())
    3673            4 :                     + "\", missing name before the assignment operator?");
    3674            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3675            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3676              : 
    3677            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3678            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3679              : 
    3680            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3681            1 :         CATCH_REQUIRE(sections.size() == 2);
    3682            3 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3683            3 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    3684              : 
    3685            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3686              : 
    3687            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3688            3 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    3689              : 
    3690            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3691            3 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "142");
    3692            1 :     }
    3693            5 :     CATCH_END_SECTION()
    3694              : 
    3695            5 :     CATCH_START_SECTION("invalid_field_name: empty variable name after section name")
    3696              :     {
    3697            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "section-and-name-missing");
    3698              : 
    3699              :         {
    3700            1 :             std::ofstream config_file;
    3701            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3702            1 :             CATCH_REQUIRE(config_file.good());
    3703              :             config_file <<
    3704              :                 "# Auto-generated\n"
    3705              :                 "a..b=red\n"
    3706              :                 "a.b.=color\n"                  // <-- name missing after section name
    3707            1 :                 "a.b.c=142\n"
    3708              :             ;
    3709            1 :         }
    3710              : 
    3711            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3712              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3713              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3714              :                             , advgetopt::COMMENT_SHELL
    3715            1 :                             , advgetopt::SECTION_OPERATOR_C);
    3716              : 
    3717            1 :         CATCH_REQUIRE(setup.is_valid());
    3718            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3719            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3720            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3721            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3722              : 
    3723            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3724              :                       "error: option name \"a.b.\" cannot end with a section operator or be empty.");
    3725            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3726            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3727              : 
    3728            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3729            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3730              : 
    3731            1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3732            1 :         CATCH_REQUIRE(sections.size() == 2);
    3733            3 :         CATCH_REQUIRE(sections.find("a") != sections.end());
    3734            3 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    3735              : 
    3736            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3737              : 
    3738            3 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3739            3 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    3740              : 
    3741            3 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3742            3 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "142");
    3743            1 :     }
    3744            5 :     CATCH_END_SECTION()
    3745              : 
    3746            5 :     CATCH_START_SECTION("invalid_field_name: variable name starts with a dash")
    3747              :     {
    3748            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "dash-name");
    3749              : 
    3750              :         {
    3751            1 :             std::ofstream config_file;
    3752            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3753            1 :             CATCH_REQUIRE(config_file.good());
    3754              :             config_file <<
    3755              :                 "# Auto-generated\n"
    3756              :                 "a=color\n"
    3757              :                 "-bad-dash=reddish\n"            // <-- name starts with '-'
    3758            1 :                 "size=412\n"
    3759              :             ;
    3760            1 :         }
    3761              : 
    3762            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3763              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3764              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3765              :                             , advgetopt::COMMENT_SHELL
    3766            1 :                             , advgetopt::SECTION_OPERATOR_C);
    3767              : 
    3768            1 :         CATCH_REQUIRE(setup.is_valid());
    3769            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3770            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3771            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3772            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3773              : 
    3774            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3775            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3776              :                       "error: option names in configuration files cannot"
    3777              :                       " start with a dash or an underscore in"
    3778              :                       " \"-bad-dash=reddish\" on line 3 from configuration file \""
    3779            4 :                     + std::string(fn.get())
    3780            4 :                     + "\".");
    3781            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3782            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3783              : 
    3784            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3785            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3786              : 
    3787            1 :         CATCH_REQUIRE(file->get_sections().empty());
    3788              : 
    3789            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3790              : 
    3791            3 :         CATCH_REQUIRE(file->has_parameter("a"));
    3792            3 :         CATCH_REQUIRE(file->has_parameter("size"));
    3793              : 
    3794            3 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3795            3 :         CATCH_REQUIRE(file->get_parameter("size") == "412");
    3796            1 :     }
    3797            5 :     CATCH_END_SECTION()
    3798              : 
    3799            5 :     CATCH_START_SECTION("invalid_field_name: variable name starts with an underscore")
    3800              :     {
    3801            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "underscore-name");
    3802              : 
    3803              :         {
    3804            1 :             std::ofstream config_file;
    3805            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3806            1 :             CATCH_REQUIRE(config_file.good());
    3807              :             config_file <<
    3808              :                 "# Auto-generated\n"
    3809              :                 "a_variable=color\n"
    3810              :                 "_bad_underscore=reddish\n"        // <-- name starts with '_'
    3811            1 :                 "pos_and_size=412x33+32-18\n"
    3812              :             ;
    3813            1 :         }
    3814              : 
    3815            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3816              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3817              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3818              :                             , advgetopt::COMMENT_SHELL
    3819            1 :                             , advgetopt::SECTION_OPERATOR_C);
    3820              : 
    3821            1 :         CATCH_REQUIRE(setup.is_valid());
    3822            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3823            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3824            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3825            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3826              : 
    3827            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3828            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3829              :                       "error: option names in configuration files cannot"
    3830              :                       " start with a dash or an underscore in"
    3831              :                       " \"_bad_underscore=reddish\" on line 3 from configuration file \""
    3832            4 :                     + std::string(fn.get())
    3833            4 :                     + "\".");
    3834            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3835            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3836              : 
    3837            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3838            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3839              : 
    3840            1 :         CATCH_REQUIRE(file->get_sections().empty());
    3841              : 
    3842            1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3843              : 
    3844            3 :         CATCH_REQUIRE(file->has_parameter("a-variable"));
    3845            3 :         CATCH_REQUIRE(file->has_parameter("pos-and-size"));
    3846              : 
    3847            3 :         CATCH_REQUIRE(file->get_parameter("a-variable") == "color");
    3848            3 :         CATCH_REQUIRE(file->get_parameter("pos-and-size") == "412x33+32-18");
    3849            1 :     }
    3850            5 :     CATCH_END_SECTION()
    3851              : 
    3852            5 :     CATCH_START_SECTION("invalid_field_name: variable name with spaces")
    3853              :     {
    3854            5 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "name-space-more-name");
    3855              : 
    3856              :         {
    3857            1 :             std::ofstream config_file;
    3858            1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3859            1 :             CATCH_REQUIRE(config_file.good());
    3860              :             config_file <<
    3861              :                 "# Auto-generated\n"
    3862              :                 "a variable=color\n"
    3863              :                 "bad space=reddish\n"
    3864            1 :                 "pos and size=412x33+32-18\n"
    3865              :             ;
    3866            1 :         }
    3867              : 
    3868            1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3869              :                             , advgetopt::line_continuation_t::line_continuation_unix
    3870              :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3871              :                             , advgetopt::COMMENT_SHELL
    3872            1 :                             , advgetopt::SECTION_OPERATOR_C);
    3873              : 
    3874            1 :         CATCH_REQUIRE(setup.is_valid());
    3875            1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3876            1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3877            1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3878            1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3879              : 
    3880            1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3881            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3882              :                       "error: option name from \"a variable=color\" on line"
    3883              :                       " 2 in configuration file \""
    3884            4 :                     + std::string(fn.get())
    3885            4 :                     + "\" cannot include a space, missing assignment operator?");
    3886            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3887              :                       "error: option name from \"bad space=reddish\" on line"
    3888              :                       " 3 in configuration file \""
    3889            4 :                     + std::string(fn.get())
    3890            4 :                     + "\" cannot include a space, missing assignment operator?");
    3891            2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3892              :                       "error: option name from \"pos and size=412x33+32-18\" on line"
    3893              :                       " 4 in configuration file \""
    3894            4 :                     + std::string(fn.get())
    3895            4 :                     + "\" cannot include a space, missing assignment operator?");
    3896            1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3897            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3898              : 
    3899            1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3900            1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3901              : 
    3902            1 :         CATCH_REQUIRE(file->get_sections().empty());
    3903              : 
    3904            1 :         CATCH_REQUIRE(file->get_parameters().empty());
    3905            1 :     }
    3906            5 :     CATCH_END_SECTION()
    3907            5 : }
    3908              : 
    3909              : 
    3910              : 
    3911              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions