11#include "../string_func.h"
12#include "../strings_type.h"
13#include "../misc/getoptdata.h"
14#include "../ini_type.h"
15#include "../core/mem_func.hpp"
16#include "../error_func.h"
20#include "../safeguards.h"
29 fmt::print(stderr,
"settingsgen: FATAL: {}\n", msg);
50 size_t Add(
const char *text,
size_t length)
55 this->
size += store_size;
65 if (fwrite(this->
data, 1, this->
size, out_fp) != this->
size) {
66 FatalError(
"Cannot write output");
102 void Add(
const char *text,
size_t length = 0)
104 if (length == 0) length = strlen(text);
108 length -= stored_size;
114 size_t stored_size = block.
Add(text, length);
115 length -= stored_size;
127 out_data.Write(out_fp);
139 return num_blocks > 0 && this->
output_buffer[num_blocks - 1].HasRoom();
164 if (!in.has_value())
return in;
166 fseek(*in, 0L, SEEK_END);
168 fseek(*in, 0L, SEEK_SET);
173 void ReportFileError(
const char *
const pre,
const char *
const buffer,
const char *
const post)
override
175 FatalError(
"{}{}{}", pre, buffer, post);
198 if (!item.name.empty()) {
216 if (item ==
nullptr && defaults !=
nullptr) item = defaults->
GetItem(name);
217 if (item ==
nullptr || !item->
value.has_value())
return nullptr;
218 return item->
value->c_str();
230 static const int MAX_VAR_LENGTH = 64;
233 static const auto pp_lines = {
"if",
"ifdef",
"ifndef"};
235 for (
const auto &name : pp_lines) {
236 const char *condition =
FindItemValue(name, grp, default_grp);
237 if (condition !=
nullptr) {
241 output.
Add(condition);
248 const char *txt = item->
value->c_str();
249 while (*txt !=
'\0') {
263 char variable[MAX_VAR_LENGTH];
265 while (i < MAX_VAR_LENGTH - 1) {
266 if (!(txt[i] ==
'_' || (txt[i] >=
'a' && txt[i] <=
'z') || (txt[i] >=
'0' && txt[i] <=
'9')))
break;
267 variable[i] = txt[i];
275 const char *valitem =
FindItemValue(variable, grp, default_grp);
276 if (valitem !=
nullptr) output.
Add(valitem);
283 output.
Add(
"#endif\n");
299 if (templates_grp ==
nullptr)
return;
304 if (std::ranges::find(special_group_names, grp.name) != std::end(special_group_names))
continue;
307 if (template_item ==
nullptr || !template_item->
value.has_value()) {
308 FatalError(
"Cannot find template {}", grp.name);
312 if (validation_grp !=
nullptr) {
313 const IniItem *validation_item = validation_grp->
GetItem(grp.name);
314 if (validation_item !=
nullptr && validation_item->
value.has_value()) {
328 if (fname ==
nullptr)
return;
331 if (!in_fp.has_value()) {
332 FatalError(
"Cannot open file {} for copying", fname);
338 length = fread(buffer, 1,
lengthof(buffer), *in_fp);
339 if (fwrite(buffer, 1, length, out_fp) != length) {
340 FatalError(
"Cannot copy file");
342 }
while (length ==
lengthof(buffer));
354 if (!f2.has_value())
return false;
357 if (!f1.has_value()) {
358 FatalError(
"can't open {}", n1);
365 l1 = fread(b1, 1,
sizeof(b1), *f1);
366 l2 = fread(b2, 1,
sizeof(b2), *f2);
368 if (l1 != l2 || memcmp(b1, b2, l1) != 0) {
378 { .
type =
ODF_NO_VALUE, .id =
'h', .shortname =
'h', .longname =
"--help" },
380 { .type =
ODF_HAS_VALUE, .id =
'o', .shortname =
'o', .longname =
"--output" },
381 { .type =
ODF_HAS_VALUE, .id =
'b', .shortname =
'b', .longname =
"--before" },
382 { .type =
ODF_HAS_VALUE, .id =
'a', .shortname =
'a', .longname =
"--after" },
422int CDECL
main(
int argc,
char *argv[])
424 const char *output_file =
nullptr;
425 const char *before_file =
nullptr;
426 const char *after_file =
nullptr;
435 fmt::print(
"settingsgen\n"
436 "Usage: settingsgen [options] ini-file...\n"
438 " -h, -?, --help Print this help message and exit\n"
439 " -b FILE, --before FILE Copy FILE before all settings\n"
440 " -a FILE, --after FILE Copy FILE after all settings\n"
441 " -o FILE, --output FILE Write output to FILE\n");
445 output_file = mgo.
opt;
449 after_file = mgo.
opt;
453 before_file = mgo.
opt;
457 fmt::print(stderr,
"Invalid arguments\n");
468 if (output_file ==
nullptr) {
474 static const char *
const tmp_output =
"tmp2.xxx";
477 if (!fp.has_value()) {
478 FatalError(
"Cannot open file {}", tmp_output);
486 std::error_code error_code;
489 std::filesystem::remove(tmp_output, error_code);
492 std::filesystem::rename(tmp_output, output_file, error_code);
493 if (error_code) FatalError(
"rename({}, {}) failed: {}", tmp_output, output_file, error_code.message());
505std::optional<FileHandle>
FileHandle::Open(
const std::string &filename,
const std::string &mode)
507 auto f = fopen(filename.c_str(), mode.c_str());
508 if (f ==
nullptr)
return std::nullopt;
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
Output buffer for a block of data.
size_t Add(const char *text, size_t length)
Add text to the output buffer.
bool HasRoom() const
Does the block have room for more data?
void Clear()
Prepare buffer for use.
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.
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 Add(const char *text, size_t length=0)
Add text to the output storage.
void Clear()
Clear the temporary storage.
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.
void MemCpyT(T *destination, const T *source, size_t num=1)
Type-safe version of memcpy().
static const char * VALIDATION_GROUP_NAME
Name of the group containing the validation statements.
static const char * FindItemValue(const char *name, const IniGroup *grp, const IniGroup *defaults)
Find the value of a template variable.
static void AppendFile(const char *fname, FILE *out_fp)
Append a file to the output stream.
OutputStore _stored_output
Temporary storage of the output, until all processing is done.
static void DumpGroup(const IniLoadFile &ifile, const char *const group_name)
Dump a IGT_SEQUENCE group into _stored_output.
static const char * DEFAULTS_GROUP_NAME
Name of the group containing default values for the template variables.
static const char * TEMPLATES_GROUP_NAME
Name of the group containing the templates.
static void DumpSections(const IniLoadFile &ifile)
Output all non-special sections through the template / template variable expansion system.
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 bool CompareFiles(const char *n1, const char *n2)
Compare two files for identity.
static void ProcessIniFile(const char *fname)
Process a single INI file.
void FatalErrorI(const std::string &msg)
Report a fatal error.
int CDECL main(int argc, char *argv[])
And the main program (what else?)
static const char * POSTAMBLE_GROUP_NAME
Name of the group containing the post amble.
static const char * PREAMBLE_GROUP_NAME
Name of the group containing the pre amble.
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.
ArgumentSpan arguments
Remaining command line arguments.
int GetOpt()
Find the next option.
const char * opt
Option value, if available (else nullptr).
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.
void LoadFromDisk(const std::string &filename, Subdirectory subdir)
Load the Ini file's data from the disk.
const IniGroup * GetGroup(std::string_view name) const
Get the group with the given name.
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.
SettingsIniFile(const IniGroupNameList &list_group_names={}, const IniGroupNameList &seq_group_names={})
Construct a new ini loader.
void ReportFileError(const char *const pre, const char *const buffer, const char *const post) override
Report an error about the file contents.
std::optional< FileHandle > OpenFile(const std::string &filename, Subdirectory, size_t *size) override
Open the INI file.