@@ -1472,29 +1472,35 @@ write_config_value(const GlobalConfig& cfg, std::string_view section, std::strin
14721472 std::format (" cannot open '{}'" , cfg.configFile .string ())});
14731473 std::string text = std::move (*read);
14741474
1475- std::string line = std::format (" {} = {}\n " , key, literal);
1475+ // The file's own line ending: a config.toml written on Windows by an
1476+ // editor ends its lines with `\r\n`, and a line this writer adds ends as
1477+ // the others do. A header or a key is matched before either ending.
1478+ const std::string_view eol = text.find (" \r\n " ) != std::string::npos ? " \r\n " : " \n " ;
1479+ std::string line = std::format (" {} = {}{}" , key, literal, eol);
14761480 const auto header = std::format (" [{}]" , section);
1481+ auto ends_line = [&](std::size_t at) {
1482+ return at == text.size () || text[at] == ' \n ' || (text[at] == ' \r ' && at + 1 < text.size () && text[at + 1 ] == ' \n ' );
1483+ };
14771484
14781485 // The header at the start of a line, so `[toolchain]` is not found inside
14791486 // `[toolchain.x]` or a comment.
14801487 auto sectionPos = std::string::npos;
14811488 for (auto p = text.find (header); p != std::string::npos; p = text.find (header, p + 1 ))
1482- if ((p == 0 || text[p - 1 ] == ' \n ' )
1483- && (p + header.size () == text.size () || text[p + header.size ()] == ' \n ' )) {
1489+ if ((p == 0 || text[p - 1 ] == ' \n ' ) && ends_line (p + header.size ())) {
14841490 sectionPos = p;
14851491 break ;
14861492 }
14871493 if (sectionPos == std::string::npos) {
14881494 // Append the section's block at end.
1489- if (!text.empty () && text.back () != ' \n ' ) text += ' \n ' ;
1490- text += std::format (" \n {} \n {} " , header, line);
1495+ if (!text.empty () && text.back () != ' \n ' ) text += eol ;
1496+ text += std::format (" {}{}{}{} " , eol, header, eol , line);
14911497 } else {
14921498 // Locate an existing `<key> = ...` line within the section. Match at
14931499 // line starts with a word boundary after the key, so `default` never
14941500 // matches the `default_target` line (and vice versa).
1495- auto eol = text.find (' \n ' , sectionPos);
1496- if (eol == std::string::npos) eol = text.size ();
1497- auto bodyStart = (eol == text.size ()) ? text.size () : eol + 1 ;
1501+ auto headerEnd = text.find (' \n ' , sectionPos);
1502+ if (headerEnd == std::string::npos) headerEnd = text.size ();
1503+ auto bodyStart = (headerEnd == text.size ()) ? text.size () : headerEnd + 1 ;
14981504 auto nextSec = text.find (" \n [" , bodyStart);
14991505 auto bodyEnd = (nextSec == std::string::npos) ? text.size () : nextSec;
15001506 auto body = std::string_view (text).substr (bodyStart, bodyEnd - bodyStart);
0 commit comments