11#include "../core/string_consumer.hpp"
12#include "../string_func.h"
13#include "../strings_type.h"
14#include "../misc/getoptdata.h"
15#include "../ini_type.h"
16#include "../error_func.h"
21#include "../safeguards.h"
30 fmt::print(stderr,
"settingsgen: FATAL: {}\n", msg);
51 size_t Add(std::string_view text)
54 this->
size += store_size;
51 size_t Add(std::string_view text) {
…}
64 if (fwrite(this->
data, 1, this->
size, out_fp) != this->
size) {
65 FatalError(
"Cannot write output");
100 void Add(std::string_view text)
102 if (!text.empty() && this->BufferHasRoom()) {
104 text.remove_prefix(stored_size);
106 while (!text.empty()) {
109 size_t stored_size = block.
Add(text);
110 text.remove_prefix(stored_size);
100 void Add(std::string_view text) {
…}
121 out_data.Write(out_fp);
133 return num_blocks > 0 && this->
output_buffer[num_blocks - 1].HasRoom();
158 if (!in.has_value())
return in;
160 fseek(*in, 0L, SEEK_END);
162 fseek(*in, 0L, SEEK_SET);
169 FatalError(
"{}", message);
192 if (!item.name.empty()) {
210 if (item ==
nullptr && defaults !=
nullptr) item = defaults->
GetItem(name);
211 if (item ==
nullptr)
return std::nullopt;
225 static const auto pp_lines = {
"if",
"ifdef",
"ifndef"};
227 for (
const auto &name : pp_lines) {
229 if (condition.has_value()) {
233 output.
Add(*condition);
240 static const std::string_view variable_name_characters =
"_abcdefghijklmnopqrstuvwxyz0123456789";
242 while (consumer.AnyBytesLeft()) {
243 char c = consumer.ReadChar();
244 if (c !=
'$' || consumer.ReadIf(
"$")) {
246 output.
Add(std::string_view{&c, 1});
250 std::string_view variable = consumer.ReadUntilCharNotIn(variable_name_characters);
251 if (!variable.empty()) {
254 if (valitem.has_value()) output.
Add(*valitem);
261 output.
Add(
"#endif\n");
277 if (templates_grp ==
nullptr)
return;
282 if (std::ranges::find(special_group_names, grp.name) != std::end(special_group_names))
continue;
285 if (template_item ==
nullptr || !template_item->
value.has_value()) {
286 FatalError(
"Cannot find template {}", grp.name);
290 if (validation_grp !=
nullptr) {
291 const IniItem *validation_item = validation_grp->
GetItem(grp.name);
292 if (validation_item !=
nullptr && validation_item->
value.has_value()) {
304static void AppendFile(std::optional<std::string_view> fname, FILE *out_fp)
306 if (!fname.has_value())
return;
309 if (!in_fp.has_value()) {
310 FatalError(
"Cannot open file {} for copying", *fname);
316 length = fread(buffer, 1,
lengthof(buffer), *in_fp);
317 if (fwrite(buffer, 1, length, out_fp) != length) {
318 FatalError(
"Cannot copy file");
320 }
while (length ==
lengthof(buffer));
304static void AppendFile(std::optional<std::string_view> fname, FILE *out_fp) {
…}
329static bool CompareFiles(std::filesystem::path path1, std::filesystem::path path2)
332 std::error_code error_code;
333 if (std::filesystem::file_size(path1, error_code) != std::filesystem::file_size(path2, error_code))
return false;
335 std::ifstream stream1(path1, std::ifstream::binary);
336 std::ifstream stream2(path2, std::ifstream::binary);
338 return std::equal(std::istreambuf_iterator<char>(stream1.rdbuf()),
339 std::istreambuf_iterator<char>(),
340 std::istreambuf_iterator<char>(stream2.rdbuf()));
329static bool CompareFiles(std::filesystem::path path1, std::filesystem::path path2) {
…}
345 { .
type =
ODF_NO_VALUE, .id =
'h', .shortname =
'h', .longname =
"--help" },
347 { .type =
ODF_HAS_VALUE, .id =
'o', .shortname =
'o', .longname =
"--output" },
348 { .type =
ODF_HAS_VALUE, .id =
'b', .shortname =
'b', .longname =
"--before" },
349 { .type =
ODF_HAS_VALUE, .id =
'a', .shortname =
'a', .longname =
"--after" },
389int CDECL
main(
int argc,
char *argv[])
391 std::optional<std::string_view> output_file;
392 std::optional<std::string_view> before_file;
393 std::optional<std::string_view> after_file;
395 std::vector<std::string_view> params;
396 for (
int i = 1; i < argc; ++i) params.emplace_back(argv[i]);
404 fmt::print(
"settingsgen\n"
405 "Usage: settingsgen [options] ini-file...\n"
407 " -h, -?, --help Print this help message and exit\n"
408 " -b FILE, --before FILE Copy FILE before all settings\n"
409 " -a FILE, --after FILE Copy FILE after all settings\n"
410 " -o FILE, --output FILE Write output to FILE\n");
414 output_file = mgo.
opt;
418 after_file = mgo.
opt;
422 before_file = mgo.
opt;
426 fmt::print(stderr,
"Invalid arguments\n");
437 if (!output_file.has_value()) {
443 static const std::string_view tmp_output =
"tmp2.xxx";
446 if (!fp.has_value()) {
447 FatalError(
"Cannot open file {}", tmp_output);
455 std::error_code error_code;
458 std::filesystem::remove(tmp_output, error_code);
461 std::filesystem::rename(tmp_output, *output_file, error_code);
462 if (error_code) FatalError(
"rename({}, {}) failed: {}", tmp_output, *output_file, error_code.message());
389int CDECL
main(
int argc,
char *argv[]) {
…}
474std::optional<FileHandle>
FileHandle::Open(
const std::string &filename, std::string_view mode)
476 auto f = fopen(filename.c_str(), std::string{mode}.c_str());
477 if (f ==
nullptr)
return std::nullopt;
static std::optional< FileHandle > Open(const std::string &filename, std::string_view mode)
Open an RAII file handle if possible.
Output buffer for a block of data.
bool HasRoom() const
Does the block have room for more data?
void Clear()
Prepare buffer for use.
size_t Add(std::string_view text)
Add text to the output buffer.
size_t size
Number of bytes stored in data.
void Write(FILE *out_fp) const
Dump buffer to the output stream.
char data[OUTPUT_BLOCK_SIZE]
Stored data.
Temporarily store output.
std::vector< OutputBuffer > OutputBufferVector
Vector type for output buffers.
OutputBufferVector output_buffer
Vector of blocks containing the stored output.
void Add(std::string_view text)
Add text to the output storage.
bool BufferHasRoom() const
Does the buffer have room without adding a new OutputBuffer block?
void Write(FILE *out_fp) const
Write all stored output to the output stream.
void Clear()
Clear the temporary storage.
Parse data from a string / buffer.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ NO_DIRECTORY
A path without any base directory.
@ ODF_NO_VALUE
A plain option (no value attached to it).
@ ODF_HAS_VALUE
An option with a value.
@ IGT_SEQUENCE
A list of uninterpreted lines, terminated by the next group block.
OutputStore _stored_output
Temporary storage of the output, until all processing is done.
static const std::string_view VALIDATION_GROUP_NAME
Name of the group containing the validation statements.
static void DumpSections(const IniLoadFile &ifile)
Output all non-special sections through the template / template variable expansion system.
static std::optional< std::string_view > FindItemValue(std::string_view name, const IniGroup *grp, const IniGroup *defaults)
Find the value of a template variable.
static void DumpLine(const IniItem *item, const IniGroup *grp, const IniGroup *default_grp, OutputStore &output)
Parse a single entry via a template and output this.
OutputStore _post_amble_output
Similar to _stored_output, but for the post amble.
static const OptionData _opts[]
Options of settingsgen.
static const std::string_view POSTAMBLE_GROUP_NAME
Name of the group containing the post amble.
static const std::string_view PREAMBLE_GROUP_NAME
Name of the group containing the pre amble.
void FatalErrorI(const std::string &msg)
Report a fatal error.
int CDECL main(int argc, char *argv[])
And the main program (what else?)
static bool CompareFiles(std::filesystem::path path1, std::filesystem::path path2)
Compare two files for identity.
static void AppendFile(std::optional< std::string_view > fname, FILE *out_fp)
Append a file to the output stream.
static const std::string_view DEFAULTS_GROUP_NAME
Name of the group containing default values for the template variables.
static void ProcessIniFile(std::string_view fname)
Process a single INI file.
static void DumpGroup(const IniLoadFile &ifile, std::string_view group_name)
Dump a IGT_SEQUENCE group into _stored_output.
static const std::string_view TEMPLATES_GROUP_NAME
Name of the group containing the templates.
static const size_t OUTPUT_BLOCK_SIZE
Block size of the buffer in OutputBuffer.
#define lengthof(array)
Return the length of an fixed size array.
Data storage for parsing command line options.
std::string_view opt
Option value, if available (else empty).
ArgumentSpan arguments
Remaining command line arguments.
int GetOpt()
Find the next option.
A group within an ini file.
const IniItem * GetItem(std::string_view name) const
Get the item with the given name.
IniGroupType type
type of group
std::list< IniItem > items
all items in the group
A single "line" in an ini file.
std::optional< std::string > value
The value of this item.
Ini file that only supports loading.
std::list< IniGroup > groups
all groups in the ini
const IniGroupNameList seq_group_names
list of group names that are sequences.
const IniGroup * GetGroup(std::string_view name) const
Get the group with the given name.
void LoadFromDisk(std::string_view filename, Subdirectory subdir)
Load the Ini file's data from the disk.
const IniGroupNameList list_group_names
list of group names that are lists
OptionDataType type
The type of option.
Derived class for loading INI files without going through Fio stuff.
void ReportFileError(std::string_view message) override
Report an error about the file contents.
SettingsIniFile(const IniGroupNameList &list_group_names={}, const IniGroupNameList &seq_group_names={})
Construct a new ini loader.
std::optional< FileHandle > OpenFile(std::string_view filename, Subdirectory, size_t *size) override
Open the INI file.