Changeset - r13903:5fb1706b1ba1
[Not reviewed]
master
0 1 0
rubidium - 15 years ago 2009-12-09 11:41:27
rubidium@openttd.org
(svn r18444) -Feature: allow G and P to 'select' substrings of STRINGn for getting their gender
1 file changed with 21 insertions and 8 deletions:
0 comments (0 inline, 0 general)
src/strgen/strgen.cpp
Show inline comments
 
@@ -224,189 +224,197 @@ size_t Utf8Validate(const char *s)
 
		if (c >= 0x800) return 3;
 
	} else if (GB(s[0], 3, 5) == 30 && IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
 
		/* 4 bytes */
 
		c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6);
 
		if (c >= 0x10000 && c <= 0x10FFFF) return 4;
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
static void EmitSingleChar(char *buf, int value)
 
{
 
	if (*buf != '\0') strgen_warning("Ignoring trailing letters in command");
 
	PutUtf8(value);
 
}
 

	
 

	
 
/* The plural specifier looks like
 
 * {NUM} {PLURAL -1 passenger passengers} then it picks either passenger/passengers depending on the count in NUM */
 

	
 
/* This is encoded like
 
 *  CommandByte <ARG#> <NUM> {Length of each string} {each string} */
 

	
 
bool ParseRelNum(char **buf, int *value)
 
bool ParseRelNum(char **buf, int *value, int *offset)
 
{
 
	const char *s = *buf;
 
	char *end;
 
	bool rel = false;
 

	
 
	while (*s == ' ' || *s == '\t') s++;
 
	if (*s == '+') {
 
		rel = true;
 
		s++;
 
	}
 
	int v = strtol(s, &end, 0);
 
	if (end == s) return false;
 
	if (rel || v < 0) {
 
		*value += v;
 
	} else {
 
		*value = v;
 
	}
 
	if (offset != NULL && *end == ':') {
 
		/* Take the Nth within */
 
		s = end + 1;
 
		*offset = strtol(s, &end, 0);
 
		if (end == s) return false;
 
	}
 
	*buf = end;
 
	return true;
 
}
 

	
 
/* Parse out the next word, or NULL */
 
char *ParseWord(char **buf)
 
{
 
	char *s = *buf, *r;
 

	
 
	while (*s == ' ' || *s == '\t') s++;
 
	if (*s == '\0') return NULL;
 

	
 
	if (*s == '"') {
 
		r = ++s;
 
		/* parse until next " or NUL */
 
		for (;;) {
 
			if (*s == '\0') break;
 
			if (*s == '"') {
 
				*s++ = '\0';
 
				break;
 
			}
 
			s++;
 
		}
 
	} else {
 
		/* proceed until whitespace or NUL */
 
		r = s;
 
		for (;;) {
 
			if (*s == '\0') break;
 
			if (*s == ' ' || *s == '\t') {
 
				*s++ = '\0';
 
				break;
 
			}
 
			s++;
 
		}
 
	}
 
	*buf = s;
 
	return r;
 
}
 

	
 
/* Forward declaration */
 
static int TranslateArgumentIdx(int arg);
 
static int TranslateArgumentIdx(int arg, int offset = 0);
 

	
 
static void EmitWordList(const char * const *words, uint nw)
 
{
 
	PutByte(nw);
 
	for (uint i = 0; i < nw; i++) PutByte(strlen(words[i]) + 1);
 
	for (uint i = 0; i < nw; i++) {
 
		for (uint j = 0; words[i][j] != '\0'; j++) PutByte(words[i][j]);
 
		PutByte(0);
 
	}
 
}
 

	
 
static void EmitPlural(char *buf, int value)
 
{
 
	int argidx = _cur_argidx;
 
	int offset = 0;
 
	const char *words[5];
 
	int nw = 0;
 

	
 
	/* Parse out the number, if one exists. Otherwise default to prev arg. */
 
	if (!ParseRelNum(&buf, &argidx)) argidx--;
 
	if (!ParseRelNum(&buf, &argidx, &offset)) argidx--;
 

	
 
	/* Parse each string */
 
	for (nw = 0; nw < 5; nw++) {
 
		words[nw] = ParseWord(&buf);
 
		if (words[nw] == NULL) break;
 
	}
 

	
 
	if (nw == 0) {
 
		error("%s: No plural words", _cur_ident);
 
	}
 

	
 
	if (_plural_forms[_lang_pluralform].plural_count != nw) {
 
		if (_translated) {
 
			error("%s: Invalid number of plural forms. Expecting %d, found %d.", _cur_ident,
 
				_plural_forms[_lang_pluralform].plural_count, nw);
 
		} else {
 
			if ((_show_todo & 2) != 0) strgen_warning("'%s' is untranslated. Tweaking english string to allow compilation for plural forms", _cur_ident);
 
			if (nw > _plural_forms[_lang_pluralform].plural_count) {
 
				nw = _plural_forms[_lang_pluralform].plural_count;
 
			} else {
 
				for (; nw < _plural_forms[_lang_pluralform].plural_count; nw++) {
 
					words[nw] = words[nw - 1];
 
				}
 
			}
 
		}
 
	}
 

	
 
	PutUtf8(SCC_PLURAL_LIST);
 
	PutByte(TranslateArgumentIdx(argidx));
 
	PutByte(TranslateArgumentIdx(argidx, offset));
 
	EmitWordList(words, nw);
 
}
 

	
 

	
 
static void EmitGender(char *buf, int value)
 
{
 
	int argidx = _cur_argidx;
 
	int offset = 0;
 
	uint nw;
 

	
 
	if (buf[0] == '=') {
 
		buf++;
 

	
 
		/* This is a {G=DER} command */
 
		for (nw = 0; ; nw++) {
 
			if (nw >= MAX_NUM_GENDER) error("G argument '%s' invalid", buf);
 
			if (strcmp(buf, _genders[nw]) == 0) break;
 
		}
 
		/* now nw contains the gender index */
 
		PutUtf8(SCC_GENDER_INDEX);
 
		PutByte(nw);
 
	} else {
 
		const char *words[MAX_NUM_GENDER];
 

	
 
		/* This is a {G 0 foo bar two} command.
 
		 * If no relative number exists, default to +0 */
 
		if (!ParseRelNum(&buf, &argidx)) {}
 
		if (!ParseRelNum(&buf, &argidx, &offset)) {}
 

	
 
		for (nw = 0; nw < MAX_NUM_GENDER; nw++) {
 
			words[nw] = ParseWord(&buf);
 
			if (words[nw] == NULL) break;
 
		}
 
		if (nw != _numgenders) error("Bad # of arguments for gender command");
 
		PutUtf8(SCC_GENDER_LIST);
 
		PutByte(TranslateArgumentIdx(argidx));
 
		PutByte(TranslateArgumentIdx(argidx, offset));
 
		EmitWordList(words, nw);
 
	}
 
}
 

	
 
static const CmdStruct *FindCmd(const char *s, int len)
 
{
 
	for (const CmdStruct *cs = _cmd_structs; cs != endof(_cmd_structs); cs++) {
 
		if (strncmp(cs->cmd, s, len) == 0 && cs->cmd[len] == '\0') return cs;
 
	}
 
	return NULL;
 
}
 

	
 
static uint ResolveCaseName(const char *str, uint len)
 
{
 
	for (uint i = 0; i < MAX_NUM_CASES; i++) {
 
		if (memcmp(_cases[i], str, len) == 0 && _cases[i][len] == 0) return i + 1;
 
	}
 
	error("Invalid case-name '%s'", str);
 
}
 

	
 

	
 
/* returns NULL on eof
 
 * else returns command struct */
 
static const CmdStruct *ParseCommandString(const char **str, char *param, int *argno, int *casei)
 
@@ -927,62 +935,67 @@ static void WriteStringsH(const char *fi
 

	
 
	fprintf(out,
 
		"\nenum {\n"
 
		"\tLANGUAGE_PACK_IDENT = 0x474E414C, // Big Endian value for 'LANG' (LE is 0x 4C 41 4E 47)\n"
 
		"\tLANGUAGE_PACK_VERSION = 0x%X,\n"
 
		"};\n", (uint)_hash
 
	);
 

	
 
	fprintf(out, "\n#endif /* TABLE_STRINGS_H */\n");
 

	
 
	fclose(out);
 

	
 
	if (CompareFiles("tmp.xxx", filename)) {
 
		/* files are equal. tmp.xxx is not needed */
 
		unlink("tmp.xxx");
 
	} else {
 
		/* else rename tmp.xxx into filename */
 
#if defined(WIN32) || defined(WIN64)
 
		unlink(filename);
 
#endif
 
		if (rename("tmp.xxx", filename) == -1) error("rename() failed");
 
	}
 
}
 

	
 
static int TranslateArgumentIdx(int argidx)
 
static int TranslateArgumentIdx(int argidx, int offset)
 
{
 
	int sum;
 

	
 
	if (argidx < 0 || (uint)argidx >= lengthof(_cur_pcs.cmd)) {
 
		error("invalid argidx %d", argidx);
 
	}
 
	const CmdStruct *cs = _cur_pcs.cmd[argidx];
 
	if (cs != NULL && cs->consumes <= offset) {
 
		error("invalid argidx offset %d:%d\n", argidx, offset);
 
	}
 

	
 
	for (int i = sum = 0; i < argidx; i++) {
 
		const CmdStruct *cs = _cur_pcs.cmd[i];
 

	
 
		sum += (cs != NULL) ? cs->consumes : 1;
 
	}
 

	
 
	return sum;
 
	return sum + offset;
 
}
 

	
 
static void PutArgidxCommand()
 
{
 
	PutUtf8(SCC_ARG_INDEX);
 
	PutByte(TranslateArgumentIdx(_cur_argidx));
 
}
 

	
 

	
 
static void PutCommandString(const char *str)
 
{
 
	_cur_argidx = 0;
 

	
 
	while (*str != '\0') {
 
		/* Process characters as they are until we encounter a { */
 
		if (*str != '{') {
 
			PutByte(*str++);
 
			continue;
 
		}
 

	
 
		char param[256];
 
		int argno;
 
		int casei;
 
		const CmdStruct *cs = ParseCommandString(&str, param, &argno, &casei);
0 comments (0 inline, 0 general)