changeset 18724:fc2ada8c1779 draft

(svn r23572) -Codechange: split actual file reading from logic for parsing
author rubidium <rubidium@openttd.org>
date Sat, 17 Dec 2011 16:56:32 +0000
parents 8469a2691595
children 495cef967740
files src/strgen/strgen.cpp
diffstat 1 files changed, 121 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/src/strgen/strgen.cpp
+++ b/src/strgen/strgen.cpp
@@ -642,11 +642,102 @@
 	return cmd;
 }
 
+/** Helper for reading strings. */
+struct StringReader {
+	StringData &data; ///< The data to fill during reading.
+	const char *file; ///< The file we are reading.
+	bool master;      ///< Are we reading the master file?
+	bool translation; ///< Are we reading a translation, implies !master. However, the base translation will have this false.
 
-static void HandlePragma(StringData &data, char *str, bool master)
+	/**
+	 * Prepare reading.
+	 * @param data        The data to fill during reading.
+	 * @param file        The file we are reading.
+	 * @param master      Are we reading the master file?
+	 * @param translation Are we reading a translation?
+	 */
+	StringReader(StringData &data, const char *file, bool master, bool translation) :
+			data(data), file(strdup(file)), master(master), translation(translation)
+	{
+	}
+
+	/** Make sure the right reader gets freed. */
+	virtual ~StringReader()
+	{
+		free(file);
+	}
+
+	/**
+	 * Read a single line from the source of strings.
+	 * @param buffer The buffer to read the data in to.
+	 * @param size   The size of the buffer.
+	 * @return The buffer, or NULL if at the end of the file.
+	 */
+	virtual char *ReadLine(char *buffer, size_t size) = 0;
+
+	/**
+	 * Handle the pragma of the file.
+	 * @param str    The pragma string to parse.
+	 */
+	virtual void HandlePragma(char *str) = 0;
+
+	/**
+	 * Handle reading a single string.
+	 * @param str The string to handle.
+	 */
+	void HandleString(char *str);
+
+	/**
+	 * Start parsing the file.
+	 */
+	virtual void ParseFile();
+};
+
+/** A reader that simply reads using fopen. */
+struct FileStringReader : StringReader {
+	FILE *fh; ///< The file we are reading.
+
+	/**
+	 * Create the reader.
+	 * @param data        The data to fill during reading.
+	 * @param file        The file we are reading.
+	 * @param master      Are we reading the master file?
+	 * @param translation Are we reading a translation?
+	 */
+	FileStringReader(StringData &data, const char *file, bool master, bool translation) :
+			StringReader(data, file, master, translation)
+	{
+		this->fh = fopen(file, "rb");
+		if (this->fh == NULL) error("Could not open %s", file);
+	}
+
+	/** Free/close the file. */
+	virtual ~FileStringReader()
+	{
+		fclose(this->fh);
+	}
+
+	/* virtual */ char *ReadLine(char *buffer, size_t size)
+	{
+		return fgets(buffer, size, this->fh);
+	}
+
+	/* virtual */ void HandlePragma(char *str);
+
+	/* virtual */ void ParseFile()
+	{
+		this->StringReader::ParseFile();
+
+		if (StrEmpty(_lang.name) || StrEmpty(_lang.own_name) || StrEmpty(_lang.isocode)) {
+			error("Language must include ##name, ##ownname and ##isocode");
+		}
+	}
+};
+
+void FileStringReader::HandlePragma(char *str)
 {
 	if (!memcmp(str, "id ", 3)) {
-		data.next_string_id = strtoul(str + 3, NULL, 0);
+		this->data.next_string_id = strtoul(str + 3, NULL, 0);
 	} else if (!memcmp(str, "name ", 5)) {
 		strecpy(_lang.name, str + 5, lastof(_lang.name));
 	} else if (!memcmp(str, "ownname ", 8)) {
@@ -690,7 +781,7 @@
 		}
 		_lang.newgrflangid = (uint8)langid;
 	} else if (!memcmp(str, "gender ", 7)) {
-		if (master) error("Genders are not allowed in the base translation.");
+		if (this->master) error("Genders are not allowed in the base translation.");
 		char *buf = str + 7;
 
 		for (;;) {
@@ -702,7 +793,7 @@
 			_lang.num_genders++;
 		}
 	} else if (!memcmp(str, "case ", 5)) {
-		if (master) error("Cases are not allowed in the base translation.");
+		if (this->master) error("Cases are not allowed in the base translation.");
 		char *buf = str + 5;
 
 		for (;;) {
@@ -823,10 +914,10 @@
 	return result;
 }
 
-static void HandleString(StringData &data, char *str, bool master)
+void StringReader::HandleString(char *str)
 {
 	if (*str == '#') {
-		if (str[1] == '#' && str[2] != '#') HandlePragma(data, str + 2, master);
+		if (str[1] == '#' && str[2] != '#') this->HandlePragma(str + 2);
 		return;
 	}
 
@@ -869,9 +960,9 @@
 	if (casep != NULL) *casep++ = '\0';
 
 	/* Check if this string already exists.. */
-	LangString *ent = data.Find(str);
+	LangString *ent = this->data.Find(str);
 
-	if (master) {
+	if (this->master) {
 		if (casep != NULL) {
 			strgen_error("Cases in the base translation are not supported.");
 			return;
@@ -882,13 +973,13 @@
 			return;
 		}
 
-		if (data.strings[data.next_string_id] != NULL) {
-			strgen_error("String ID 0x%X for '%s' already in use by '%s'", data.next_string_id, str, data.strings[data.next_string_id]->name);
+		if (this->data.strings[this->data.next_string_id] != NULL) {
+			strgen_error("String ID 0x%X for '%s' already in use by '%s'", this->data.next_string_id, str, this->data.strings[this->data.next_string_id]->name);
 			return;
 		}
 
 		/* Allocate a new LangString */
-		data.Add(str, new LangString(str, s, data.next_string_id++, _cur_line));
+		this->data.Add(str, new LangString(str, s, this->data.next_string_id++, _cur_line));
 	} else {
 		if (ent == NULL) {
 			strgen_warning("String name '%s' does not exist in master file", str);
@@ -923,37 +1014,25 @@
 	buf[i] = '\0';
 }
 
-
-static void ParseFile(StringData &data, const char *file, bool english)
+void StringReader::ParseFile()
 {
-	FILE *in;
 	char buf[2048];
 
-	/* Only look at the final filename to determine whether it's the base language or not */
-	const char *cur_file = strrchr(_file, PATHSEPCHAR);
-	const char *next_file = strrchr(file, PATHSEPCHAR);
-	_translation = next_file != NULL && cur_file != NULL && strcmp(cur_file, next_file) != 0;
-	_file = file;
+	_translation = this->master || this->translation;
+	_file = this->file;
 
-	/* For each new file we parse, reset the genders, and language codes */
+	/* For each new file we parse, reset the genders, and language codes. */
 	MemSetT(&_lang, 0);
 	strecpy(_lang.digit_group_separator, ",", lastof(_lang.digit_group_separator));
 	strecpy(_lang.digit_group_separator_currency, ",", lastof(_lang.digit_group_separator_currency));
 	strecpy(_lang.digit_decimal_separator, ".", lastof(_lang.digit_decimal_separator));
 
-	in = fopen(file, "r");
-	if (in == NULL) error("Cannot open file");
 	_cur_line = 1;
-	while (fgets(buf, sizeof(buf), in) != NULL) {
+	while (this->ReadLine(buf, sizeof(buf)) != NULL) {
 		rstrip(buf);
-		HandleString(data, buf, english);
+		this->HandleString(buf);
 		_cur_line++;
 	}
-	fclose(in);
-
-	if (StrEmpty(_lang.name) || StrEmpty(_lang.own_name) || StrEmpty(_lang.isocode)) {
-		error("Language must include ##name, ##ownname and ##isocode");
-	}
 }
 
 bool CompareFiles(const char *n1, const char *n2)
@@ -1523,15 +1602,16 @@
 
 	try {
 		/* strgen has two modes of operation. If no (free) arguments are passed
-		* strgen generates strings.h to the destination directory. If it is supplied
-		* with a (free) parameter the program will translate that language to destination
-		* directory. As input english.txt is parsed from the source directory */
+		 * strgen generates strings.h to the destination directory. If it is supplied
+		 * with a (free) parameter the program will translate that language to destination
+		 * directory. As input english.txt is parsed from the source directory */
 		if (mgo.numleft == 0) {
 			mkpath(pathbuf, lengthof(pathbuf), src_dir, "english.txt");
 
+			/* parse master file */
 			StringData data;
-			/* parse master file */
-			ParseFile(data, pathbuf, true);
+			FileStringReader master_reader(data, pathbuf, true, false);
+			master_reader.ParseFile();
 			if (_errors != 0) return 1;
 
 			/* write strings.h */
@@ -1548,8 +1628,13 @@
 
 			StringData data;
 			/* parse master file and check if target file is correct */
-			ParseFile(data, pathbuf, true);
-			ParseFile(data, replace_pathsep(mgo.argv[0]), false); // target file
+			FileStringReader master_reader(data, pathbuf, true, false);
+			master_reader.ParseFile();
+
+			const char *translation = replace_pathsep(mgo.argv[0]);
+			const char *file = strrchr(translation, PATHSEPCHAR);
+			FileStringReader translation_reader(data, translation, false, file == NULL || strcmp(file + 1, "english.txt") != 0);
+			translation_reader.ParseFile(); // target file
 			if (_errors != 0) return 1;
 
 			/* get the targetfile, strip any directories and append to destination path */