OpenTTD Source
12.0-beta2
|
Go to the documentation of this file.
10 #include "../stdafx.h"
11 #include "../core/endian_func.hpp"
12 #include "../string_func.h"
13 #include "../strings_type.h"
14 #include "../misc/getoptdata.h"
15 #include "../table/control_codes.h"
22 #if !defined(_WIN32) || defined(__CYGWIN__)
27 #if defined(_WIN32) || defined(__WATCOMC__)
31 #include "../table/strgen_tables.h"
33 #include "../safeguards.h"
37 # define LINE_NUM_FMT(s) "%s (%d): warning: %s (" s ")\n"
39 # define LINE_NUM_FMT(s) "%s:%d: " s ": %s\n"
42 void CDECL strgen_warning(
const char *s, ...)
57 void CDECL strgen_error(
const char *s, ...)
68 void NORETURN CDECL strgen_fatal(
const char *s, ...)
77 fprintf(stderr, LINE_NUM_FMT(
"warning"),
_file,
_cur_line,
"language is not compiled");
79 throw std::exception();
82 void NORETURN CDECL
error(
const char *s, ...)
91 fprintf(stderr, LINE_NUM_FMT(
"warning"),
_file,
_cur_line,
"language is not compiled");
110 this->fh = fopen(
file,
"rb");
111 if (this->fh ==
nullptr)
error(
"Could not open %s",
file);
120 char *
ReadLine(
char *buffer,
const char *last)
override
122 return fgets(buffer,
ClampToU16(last - buffer + 1), this->fh);
132 error(
"Language must include ##name, ##ownname and ##isocode");
139 if (!memcmp(str,
"id ", 3)) {
141 }
else if (!memcmp(str,
"name ", 5)) {
143 }
else if (!memcmp(str,
"ownname ", 8)) {
145 }
else if (!memcmp(str,
"isocode ", 8)) {
147 }
else if (!memcmp(str,
"textdir ", 8)) {
148 if (!memcmp(str + 8,
"ltr", 3)) {
150 }
else if (!memcmp(str + 8,
"rtl", 3)) {
153 error(
"Invalid textdir %s", str + 8);
155 }
else if (!memcmp(str,
"digitsep ", 9)) {
158 }
else if (!memcmp(str,
"digitsepcur ", 12)) {
161 }
else if (!memcmp(str,
"decimalsep ", 11)) {
164 }
else if (!memcmp(str,
"winlangid ", 10)) {
165 const char *buf = str + 10;
166 long langid = strtol(buf,
nullptr, 16);
167 if (langid > (
long)UINT16_MAX || langid < 0) {
168 error(
"Invalid winlangid %s", buf);
171 }
else if (!memcmp(str,
"grflangid ", 10)) {
172 const char *buf = str + 10;
173 long langid = strtol(buf,
nullptr, 16);
174 if (langid >= 0x7F || langid < 0) {
175 error(
"Invalid grflangid %s", buf);
178 }
else if (!memcmp(str,
"gender ", 7)) {
179 if (this->
master)
error(
"Genders are not allowed in the base translation.");
183 const char *s = ParseWord(&buf);
185 if (s ==
nullptr)
break;
190 }
else if (!memcmp(str,
"case ", 5)) {
191 if (this->
master)
error(
"Cases are not allowed in the base translation.");
195 const char *s = ParseWord(&buf);
197 if (s ==
nullptr)
break;
209 FILE *f2 = fopen(n2,
"rb");
210 if (f2 ==
nullptr)
return false;
212 FILE *f1 = fopen(n1,
"rb");
215 error(
"can't open %s", n1);
222 l1 = fread(b1, 1,
sizeof(b1), f1);
223 l2 = fread(b2, 1,
sizeof(b2), f2);
225 if (l1 != l2 || memcmp(b1, b2, l1)) {
249 this->fh = fopen(this->filename,
"wb");
251 if (this->fh ==
nullptr) {
252 error(
"Could not open %s", this->filename);
269 unlink(this->filename);
271 free(this->filename);
289 fprintf(this->
fh,
"/* This file is automatically generated. Do not modify */\n\n");
290 fprintf(this->
fh,
"#ifndef TABLE_STRINGS_H\n");
291 fprintf(this->
fh,
"#define TABLE_STRINGS_H\n");
302 if (
prev + 1 != stringid) fprintf(this->
fh,
"\n");
303 fprintf(this->
fh,
"static const StringID %s = 0x%X;\n", name, stringid);
311 int max_plural_forms = 0;
313 max_plural_forms = std::max(max_plural_forms,
_plural_forms[i].plural_count);
318 "static const uint LANGUAGE_PACK_VERSION = 0x%X;\n"
319 "static const uint LANGUAGE_MAX_PLURAL = %u;\n"
320 "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n"
321 "static const uint LANGUAGE_TOTAL_STRINGS = %u;\n"
326 fprintf(this->
fh,
"#endif /* TABLE_STRINGS_H */\n");
336 unlink(this->real_filename);
338 if (rename(this->
filename, this->real_filename) == -1)
error(
"rename() failed");
355 this->
Write((
const byte *)header,
sizeof(*header));
360 if (fputc(0, this->
fh) == EOF) {
366 void Write(
const byte *buffer,
size_t length)
368 if (fwrite(buffer,
sizeof(*buffer), length, this->
fh) != length) {
379 #if defined(_WIN32) || defined(__WATCOMC__)
382 mkdir(directory, 0755);
391 static inline char *
mkpath(
char *buf,
const char *last,
const char *path,
const char *file)
395 char *p = strchr(buf,
'\0');
396 if (p[-1] != PATHSEPCHAR && p != last) *p++ = PATHSEPCHAR;
409 static inline char *replace_pathsep(
char *s)
411 for (
char *c = s; *c !=
'\0'; c++)
if (*c ==
'/') *c =
'\\';
415 static inline char *replace_pathsep(
char *s) {
return s; }
433 int CDECL
main(
int argc,
char *argv[])
435 char pathbuf[MAX_PATH];
436 const char *src_dir =
".";
437 const char *dest_dir =
nullptr;
441 int i = mgo.GetOpt();
450 printf(
"args\tflags\tcommand\treplacement\n");
451 for (
const CmdStruct *cs = _cmd_structs; cs <
endof(_cmd_structs); cs++) {
453 if (cs->proc == EmitGender) {
455 }
else if (cs->proc == EmitPlural) {
462 printf(
"%i\t%c\t\"%s\"\t\"%s\"\n", cs->consumes, flags, cs->cmd, strstr(cs->cmd,
"STRING") ?
"STRING" : cs->cmd);
467 printf(
"count\tdescription\tnames\n");
469 printf(
"%i\t\"%s\"\t%s\n", pf->plural_count, pf->description, pf->names);
474 printf(
"name\tflags\tdefault\tdescription\n");
476 printf(
"\"%s\"\t%s\t\"%s\"\t\"%s\"\n",
491 "strgen - $Revision$\n"
492 " -v | --version print version information and exit\n"
493 " -t | --todo replace any untranslated strings with '<TODO>'\n"
494 " -w | --warning print a warning for any untranslated strings\n"
495 " -h | -? | --help print this help message and exit\n"
496 " -s | --source_dir search for english.txt in the specified directory\n"
497 " -d | --dest_dir put output file in the specified directory, create if needed\n"
498 " -export-commands export all commands and exit\n"
499 " -export-plurals export all plural forms and exit\n"
500 " -export-pragmas export all pragmas and exit\n"
501 " Run without parameters and strgen will search for english.txt and parse it,\n"
502 " creating strings.h. Passing an argument, strgen will translate that language\n"
503 " file using english.txt as a reference and output <language>.lng."
508 src_dir = replace_pathsep(mgo.opt);
512 dest_dir = replace_pathsep(mgo.opt);
516 fprintf(stderr,
"Invalid arguments\n");
521 if (dest_dir ==
nullptr) dest_dir = src_dir;
528 if (mgo.numleft == 0) {
529 mkpath(pathbuf,
lastof(pathbuf), src_dir,
"english.txt");
534 master_reader.ParseFile();
535 if (_errors != 0)
return 1;
539 mkpath(pathbuf,
lastof(pathbuf), dest_dir,
"strings.h");
542 writer.WriteHeader(data);
543 writer.Finalise(data);
544 if (_errors != 0)
return 1;
545 }
else if (mgo.numleft >= 1) {
548 mkpath(pathbuf,
lastof(pathbuf), src_dir,
"english.txt");
553 master_reader.ParseFile();
555 for (
int i = 0; i < mgo.numleft; i++) {
556 data.FreeTranslation();
558 const char *translation = replace_pathsep(mgo.argv[i]);
559 const char *file = strrchr(translation, PATHSEPCHAR);
560 FileStringReader translation_reader(data, translation,
false, file ==
nullptr || strcmp(file + 1,
"english.txt") != 0);
561 translation_reader.ParseFile();
562 if (_errors != 0)
return 1;
565 r = strrchr(mgo.argv[i], PATHSEPCHAR);
566 mkpath(pathbuf,
lastof(pathbuf), dest_dir, (r !=
nullptr) ? &r[1] : mgo.argv[i]);
569 r = strrchr(pathbuf,
'.');
570 if (r ==
nullptr || strcmp(r,
".txt") != 0) r = strchr(pathbuf,
'\0');
574 writer.WriteLang(data);
578 if ((_show_todo & 2) != 0) {
579 fprintf(stdout,
"%d warnings and %d errors for %s\n", _warnings, _errors, pathbuf);
const char * _file
The filename of the input, so we can refer to it in errors/warnings.
virtual void HandlePragma(char *str)
Handle the pragma of the file.
static const uint8 MAX_NUM_GENDERS
Maximum number of supported genders.
static bool CompareFiles(const char *n1, const char *n2)
Compare two files for identity.
@ TEXT_TAB_END
End of language files.
static const OptionData _opts[]
Options of strgen.
@ TD_LTR
Text is written left-to-right by default.
void WriteHeader(const LanguagePackHeader *header)
Write the header metadata.
Class for writing a language to disk.
#define GETOPT_VALUE(shortname, longname)
Short option with value.
LanguagePackHeader _lang
Header information about a language.
@ C_DONTCOUNT
These commands aren't counted for comparison.
int _cur_line
The current line we're parsing in the input file.
void HandlePragma(char *str) override
Handle the pragma of the file.
void Write(const byte *buffer, size_t length)
Write a number of bytes.
bool master
Are we reading the master file?
FileStringReader(StringData &data, const char *file, bool master, bool translation)
Create the reader.
size_t next_string_id
The next string ID to allocate.
#define NBSP
A non-breaking space.
const char * filename
The file name we're writing to.
uint Version() const
Make a hash of the file to get a unique "version number".
virtual ~FileStringReader()
Free/close the file.
static char * mkpath(char *buf, const char *last, const char *path, const char *file)
Create a path consisting of an already existing path, a possible path separator and the filename.
Information about the currently known strings.
#define GETOPT_NOVAL(shortname, longname)
Short option without value.
#define GETOPT_END()
Option terminator.
#define GETOPT_GENERAL(id, shortname, longname, flags)
General macro for creating an option.
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
static void ottd_mkdir(const char *directory)
Multi-OS mkdirectory function.
int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap)
Safer implementation of vsnprintf; same as vsnprintf except:
A reader that simply reads using fopen.
FileWriter(const char *filename)
Open a file to write to.
Data storage for parsing command line options.
int CDECL main(int argc, char *argv[])
And the main program (what else?)
void ParseFile() override
Start parsing the file.
static const char *const _pragmas[][4]
All pragmas used.
#define endof(x)
Get the end element of an fixed size array.
char * ReadLine(char *buffer, const char *last) override
Read a single line from the source of strings.
StringData & data
The data to fill during reading.
LanguageFileWriter(const char *filename)
Open a file to write to.
FILE * fh
The file we are reading.
bool translation
Are we reading a translation, implies !master. However, the base translation will have this false.
virtual void ParseFile()
Start parsing the file.
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
#define lengthof(x)
Return the length of an fixed size array.
Base class for all language writers.
void Finalise()
Finalise writing the file.
static uint16 ClampToU16(const uint64 a)
Reduce an unsigned 64-bit int to an unsigned 16-bit one.
const char * file
The file we are reading.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
virtual ~FileWriter()
Make sure the file is closed.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
void Finalise()
Finalise the writing.
#define lastof(x)
Get the last element of an fixed size array.
static const uint8 MAX_NUM_CASES
Maximum number of supported cases.
static const PluralForm _plural_forms[]
All plural forms used.
@ ODF_NO_VALUE
A plain option (no value attached to it).
@ TD_RTL
Text is written right-to-left by default.
Helper for reading strings.
FILE * fh
The file handle we're writing to.
Yes, simply writing to a file.