// { dg-do run { target c++20 } } // { dg-require-effective-target tzdb } // { dg-require-effective-target cxx11_abi } // { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } } #include #include #include static bool override_used = false; namespace __gnu_cxx { const char* zoneinfo_dir_override() { override_used = true; return "./"; } } using namespace std::chrono; void test_link_chains() { std::ofstream("tzdata.zi") << R"(# version test_1 Link Greenwich G_M_T Link Etc/GMT Greenwich Zone Etc/GMT 0 - GMT Zone A_Zone 1 - ZON Link A_Zone L1 Link L1 L2 Link L2 L3 Link L3 L4 Link L4 L5 Link L5 L6 Link L3 L7 )"; const auto& db = reload_tzdb(); VERIFY( override_used ); // If this fails then XFAIL for the target. VERIFY( db.version == "test_1" ); // Simple case of a link with a zone as its target. VERIFY( locate_zone("Greenwich")->name() == "Etc/GMT" ); // Chains of links, where the target may be another link. VERIFY( locate_zone("G_M_T")->name() == "Etc/GMT" ); VERIFY( locate_zone("L1")->name() == "A_Zone" ); VERIFY( locate_zone("L2")->name() == "A_Zone" ); VERIFY( locate_zone("L3")->name() == "A_Zone" ); VERIFY( locate_zone("L4")->name() == "A_Zone" ); VERIFY( locate_zone("L5")->name() == "A_Zone" ); VERIFY( locate_zone("L6")->name() == "A_Zone" ); VERIFY( locate_zone("L7")->name() == "A_Zone" ); } void test_bad_links() { // The zic(8) man page says // > the behavior is unspecified if multiple zone or link lines // > define the same name" // For libstdc++ the expected behaviour is described and tested below. std::ofstream("tzdata.zi") << R"(# version test_2 Zone A_Zone 1 - ZA Zone B_Zone 2 - ZB Link A_Zone B_Zone Link B_Zone C_Link Link C_Link D_Link Link D_Link E_Link )"; const auto& db2 = reload_tzdb(); VERIFY( override_used ); // If this fails then XFAIL for the target. VERIFY( db2.version == "test_2" ); // The standard requires locate_zone(name) to search for a zone first, // so this finds the zone B_Zone, not the link that points to zone A_Zone. VERIFY( locate_zone("B_Zone")->name() == "B_Zone" ); // And libstdc++ does the same at every step when following chained links: VERIFY( locate_zone("C_Link")->name() == "B_Zone" ); VERIFY( locate_zone("D_Link")->name() == "B_Zone" ); VERIFY( locate_zone("E_Link")->name() == "B_Zone" ); // The zic(8) man page says // > the behavior is unspecified if a chain of one or more links // > does not terminate in a Zone name. // For libstdc++ we throw std::runtime_error if locate_zone finds an // unterminated chain, including the case of a chain that includes a cycle. std::ofstream("tzdata.zi") << R"(# version test_3 Zone A_Zone 1 - ZON Link A_Zone GoodLink Link No_Zone BadLink Link LinkSelf LinkSelf Link LinkSelf Link1 Link Link1 Link2 Link Cycle2_A Cycle2_B Link Cycle2_B Cycle2_A Link Cycle3_A Cycle3_B Link Cycle3_B Cycle3_C Link Cycle3_C Cycle3_A Link Cycle3_C Cycle3_D Link Cycle4_A Cycle4_B Link Cycle4_B Cycle4_C Link Cycle4_C Cycle4_D Link Cycle4_D Cycle4_A )"; const auto& db3 = reload_tzdb(); VERIFY( db3.version == "test_3" ); // Lookup for valid links should still work even if other links are bad. VERIFY( locate_zone("GoodLink")->name() == "A_Zone" ); #if __cpp_exceptions try { locate_zone("BadLink"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("cannot locate zone: BadLink") ); } // LinkSelf forms a link cycle with itself. try { locate_zone("LinkSelf"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: LinkSelf") ); } // Any chain that leads to LinkSelf reaches a cycle. try { locate_zone("Link1"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Link1") ); } try { locate_zone("Link2"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Link2") ); } // Cycle2_A and Cycle2_B form a cycle of length two. try { locate_zone("Cycle2_A"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle2_A") ); } try { locate_zone("Cycle2_B"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle2_B") ); } // Cycle3_A, Cycle3_B and Cycle3_C form a cycle of length three. try { locate_zone("Cycle3_A"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle3_A") ); } try { locate_zone("Cycle3_B"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle3_B") ); } try { locate_zone("Cycle3_C"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle3_C") ); } // Cycle3_D isn't part of the cycle, but it leads to it. try { locate_zone("Cycle3_D"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle3_D") ); } // Cycle4_* links form a cycle of length four. try { locate_zone("Cycle4_A"); VERIFY( false ); } catch (const std::runtime_error& e) { std::string_view what(e.what()); VERIFY( what.ends_with("link cycle: Cycle4_A") ); } #endif } int main() { test_link_chains(); test_bad_links(); }