30 fmt::print(stderr,
"settingsgen: FATAL: {}\n", msg);
50 size_t Add(std::string_view text)
53 this->
size += store_size;
63 if (fwrite(this->
data, 1, this->
size, out_fp) != this->
size) {
64 FatalError(
"Cannot write output");
99 void Add(std::string_view text)
101 if (!text.empty() && this->BufferHasRoom()) {
103 text.remove_prefix(stored_size);
105 while (!text.empty()) {
108 size_t stored_size = block.
Add(text);
109 text.remove_prefix(stored_size);
120 out_data.Write(out_fp);
132 return num_blocks > 0 && this->
output_buffer[num_blocks - 1].HasRoom();
157 if (!in.has_value())
return in;
159 fseek(*in, 0L, SEEK_END);
161 fseek(*in, 0L, SEEK_SET);
168 FatalError(
"{}", message);
191 if (!item.
name.empty()) {
209 if (item ==
nullptr && defaults !=
nullptr) item = defaults->
GetItem(name);
210 if (item ==
nullptr)
return std::nullopt;
224 static const auto pp_lines = {
"if",
"ifdef",
"ifndef"};
226 for (
const auto &name : pp_lines) {
228 if (condition.has_value()) {
232 output.
Add(*condition);
239 static const std::string_view variable_name_characters =
"_abcdefghijklmnopqrstuvwxyz0123456789";
243 if (c !=
'$' || consumer.
ReadIf(
"$")) {
245 output.
Add(std::string_view{&c, 1});
250 if (!variable.empty()) {
253 if (valitem.has_value()) output.
Add(*valitem);
260 output.
Add(
"#endif\n");
276 if (templates_grp ==
nullptr)
return;
281 if (std::ranges::find(special_group_names, grp.
name) != std::end(special_group_names))
continue;
284 if (template_item ==
nullptr || !template_item->
value.has_value()) {
285 FatalError(
"Cannot find template {}", grp.
name);
289 if (validation_grp !=
nullptr) {
291 if (validation_item !=
nullptr && validation_item->
value.has_value()) {
303static void AppendFile(std::optional<std::string_view> fname, FILE *out_fp)
305 if (!fname.has_value())
return;
308 if (!in_fp.has_value()) {
309 FatalError(
"Cannot open file {} for copying", *fname);
315 length = fread(buffer, 1,
lengthof(buffer), *in_fp);
316 if (fwrite(buffer, 1, length, out_fp) != length) {
317 FatalError(
"Cannot copy file");
319 }
while (length ==
lengthof(buffer));
328static bool CompareFiles(std::filesystem::path path1, std::filesystem::path path2)
331 std::error_code error_code;
332 if (std::filesystem::file_size(path1, error_code) != std::filesystem::file_size(path2, error_code))
return false;
334 std::ifstream stream1(path1, std::ifstream::binary);
335 std::ifstream stream2(path2, std::ifstream::binary);
337 return std::equal(std::istreambuf_iterator<char>(stream1.rdbuf()),
338 std::istreambuf_iterator<char>(),
339 std::istreambuf_iterator<char>(stream2.rdbuf()));
344 { .type =
ODF_NO_VALUE, .id =
'h', .shortname =
'h', .longname =
"--help" },
346 { .type =
ODF_HAS_VALUE, .id =
'o', .shortname =
'o', .longname =
"--output" },
347 { .type =
ODF_HAS_VALUE, .id =
'b', .shortname =
'b', .longname =
"--before" },
348 { .type =
ODF_HAS_VALUE, .id =
'a', .shortname =
'a', .longname =
"--after" },
388int CDECL
main(
int argc,
char *argv[])
390 std::optional<std::string_view> output_file;
391 std::optional<std::string_view> before_file;
392 std::optional<std::string_view> after_file;
394 std::vector<std::string_view> params;
395 for (
int i = 1; i < argc; ++i) params.emplace_back(argv[i]);
403 fmt::print(
"settingsgen\n"
404 "Usage: settingsgen [options] ini-file...\n"
406 " -h, -?, --help Print this help message and exit\n"
407 " -b FILE, --before FILE Copy FILE before all settings\n"
408 " -a FILE, --after FILE Copy FILE after all settings\n"
409 " -o FILE, --output FILE Write output to FILE\n");
413 output_file = mgo.
opt;
417 after_file = mgo.
opt;
421 before_file = mgo.
opt;
425 fmt::print(stderr,
"Invalid arguments\n");
436 if (!output_file.has_value()) {
442 static const std::string_view tmp_output =
"tmp2.xxx";
445 if (!fp.has_value()) {
446 FatalError(
"Cannot open file {}", tmp_output);
454 std::error_code error_code;
457 std::filesystem::remove(tmp_output, error_code);
460 std::filesystem::rename(tmp_output, *output_file, error_code);
461 if (error_code) FatalError(
"rename({}, {}) failed: {}", tmp_output, *output_file, error_code.message());
473std::optional<FileHandle>
FileHandle::Open(
const std::string &filename, std::string_view mode)
475 auto f = fopen(filename.c_str(), std::string{mode}.c_str());
476 if (f ==
nullptr)
return std::nullopt;
477 return FileHandle(f);
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.
char ReadChar(char def='?')
Read 8-bit character, and advance reader.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
std::string_view ReadUntilCharNotIn(std::string_view chars)
Read 8-bit chars, while they are in 'chars', until they are not; and advance reader.
bool ReadIf(std::string_view str)
Check whether the next data matches 'str', and skip it.
Error reporting related functions.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ NO_DIRECTORY
A path without any base directory.
Library for parsing command-line options.
@ ODF_NO_VALUE
A plain option (no value attached to it).
@ ODF_HAS_VALUE
An option with a value.
Types related to reading/writing '*.ini' files.
@ IGT_SEQUENCE
A list of uninterpreted lines, terminated by the next group block.
A number of safeguards to prevent using unsafe methods.
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.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Functions related to low-level strings.
Types related to strings.
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::string name
name 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.
std::string name
The name 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.
IniLoadFile(const IniGroupNameList &list_group_names={}, const IniGroupNameList &seq_group_names={})
Construct a new in-memory Ini file representation.
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
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.