* Save the Ini file's data to the disk. * @param filename the file to save to. * @return true if saving succeeded. */
| 40 | * @return true if saving succeeded. |
| 41 | */ |
| 42 | bool IniFile::SaveToDisk(const std::string &filename) |
| 43 | { |
| 44 | /* |
| 45 | * First write the configuration to a (temporary) file and then rename |
| 46 | * that file. This to prevent that when OpenTTD crashes during the save |
| 47 | * you end up with a truncated configuration file. |
| 48 | */ |
| 49 | std::string file_new{ filename }; |
| 50 | file_new.append(".new"); |
| 51 | |
| 52 | std::ofstream os(OTTD2FS(file_new).c_str()); |
| 53 | if (os.fail()) return false; |
| 54 | |
| 55 | for (const IniGroup &group : this->groups) { |
| 56 | os << group.comment << "[" << group.name << "]\n"; |
| 57 | for (const IniItem &item : group.items) { |
| 58 | os << item.comment; |
| 59 | |
| 60 | /* protect item->name with quotes if needed */ |
| 61 | if (item.name.find(' ') != std::string::npos || |
| 62 | item.name[0] == '[') { |
| 63 | os << "\"" << item.name << "\""; |
| 64 | } else { |
| 65 | os << item.name; |
| 66 | } |
| 67 | |
| 68 | os << " = " << item.value.value_or("") << "\n"; |
| 69 | } |
| 70 | } |
| 71 | os << this->comment; |
| 72 | |
| 73 | os.flush(); |
| 74 | os.close(); |
| 75 | if (os.fail()) return false; |
| 76 | |
| 77 | /* |
| 78 | * POSIX (and friends) do not guarantee that when a file is closed it is |
| 79 | * flushed to the disk. So we manually flush it do disk if we have the |
| 80 | * APIs to do so. We only need to flush the data as the metadata itself |
| 81 | * (modification date etc.) is not important to us; only the real data is. |
| 82 | */ |
| 83 | #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 |
| 84 | int f = open(file_new.c_str(), O_RDWR); |
| 85 | if (f < 0) return false; |
| 86 | int ret = fdatasync(f); |
| 87 | close(f); |
| 88 | if (ret != 0) return false; |
| 89 | #endif |
| 90 | |
| 91 | std::error_code ec; |
| 92 | std::filesystem::rename(OTTD2FS(file_new), OTTD2FS(filename), ec); |
| 93 | if (ec) { |
| 94 | Debug(misc, 0, "Renaming {} to {} failed; configuration not saved: {}", file_new, filename, ec.message()); |
| 95 | } |
| 96 | |
| 97 | #ifdef __EMSCRIPTEN__ |
| 98 | EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs()); |
| 99 | #endif |
no test coverage detected