src/strgen/strgen.cpp
branchNewGRF_ports
changeset 10274 b3c58f3df92b
parent 10242 52b4a9006029
child 10724 68a692eacf22
equal deleted inserted replaced
10243:e9066a148720 10274:b3c58f3df92b
   153 # define LINE_NUM_FMT "(%d)"
   153 # define LINE_NUM_FMT "(%d)"
   154 #else
   154 #else
   155 # define LINE_NUM_FMT ":%d"
   155 # define LINE_NUM_FMT ":%d"
   156 #endif
   156 #endif
   157 
   157 
   158 static void CDECL warning(const char *s, ...)
   158 static void CDECL strgen_warning(const char *s, ...)
   159 {
   159 {
   160 	char buf[1024];
   160 	char buf[1024];
   161 	va_list va;
   161 	va_list va;
   162 	va_start(va, s);
   162 	va_start(va, s);
   163 	vsnprintf(buf, lengthof(buf), s, va);
   163 	vsnprintf(buf, lengthof(buf), s, va);
   164 	va_end(va);
   164 	va_end(va);
   165 	fprintf(stderr, "%s" LINE_NUM_FMT ": warning: %s\n", _file, _cur_line, buf);
   165 	fprintf(stderr, "%s" LINE_NUM_FMT ": warning: %s\n", _file, _cur_line, buf);
   166 	_warnings++;
   166 	_warnings++;
   167 }
   167 }
   168 
   168 
   169 void CDECL error(const char *s, ...)
   169 static void CDECL strgen_error(const char *s, ...)
   170 {
   170 {
   171 	char buf[1024];
   171 	char buf[1024];
   172 	va_list va;
   172 	va_list va;
   173 	va_start(va, s);
   173 	va_start(va, s);
   174 	vsnprintf(buf, lengthof(buf), s, va);
   174 	vsnprintf(buf, lengthof(buf), s, va);
   175 	va_end(va);
   175 	va_end(va);
   176 	fprintf(stderr, "%s" LINE_NUM_FMT ": error: %s\n", _file, _cur_line, buf);
   176 	fprintf(stderr, "%s" LINE_NUM_FMT ": error: %s\n", _file, _cur_line, buf);
   177 	_errors++;
   177 	_errors++;
   178 }
   178 }
   179 
   179 
   180 
   180 void NORETURN CDECL error(const char *s, ...)
   181 static void NORETURN CDECL fatal(const char *s, ...)
       
   182 {
   181 {
   183 	char buf[1024];
   182 	char buf[1024];
   184 	va_list va;
   183 	va_list va;
   185 	va_start(va, s);
   184 	va_start(va, s);
   186 	vsnprintf(buf, lengthof(buf), s, va);
   185 	vsnprintf(buf, lengthof(buf), s, va);
   189 	exit(1);
   188 	exit(1);
   190 }
   189 }
   191 
   190 
   192 static void PutByte(byte c)
   191 static void PutByte(byte c)
   193 {
   192 {
   194 	if (_put_pos == lengthof(_put_buf)) fatal("Put buffer too small");
   193 	if (_put_pos == lengthof(_put_buf)) error("Put buffer too small");
   195 	_put_buf[_put_pos++] = c;
   194 	_put_buf[_put_pos++] = c;
   196 }
   195 }
   197 
   196 
   198 
   197 
   199 static void PutUtf8(uint32 value)
   198 static void PutUtf8(uint32 value)
   211 		PutByte(0xF0 + GB(value, 18, 3));
   210 		PutByte(0xF0 + GB(value, 18, 3));
   212 		PutByte(0x80 + GB(value, 12, 6));
   211 		PutByte(0x80 + GB(value, 12, 6));
   213 		PutByte(0x80 + GB(value,  6, 6));
   212 		PutByte(0x80 + GB(value,  6, 6));
   214 		PutByte(0x80 + GB(value,  0, 6));
   213 		PutByte(0x80 + GB(value,  0, 6));
   215 	} else {
   214 	} else {
   216 		warning("Invalid unicode value U+0x%X", value);
   215 		strgen_warning("Invalid unicode value U+0x%X", value);
   217 	}
   216 	}
   218 }
   217 }
   219 
   218 
   220 
   219 
   221 size_t Utf8Validate(const char *s)
   220 size_t Utf8Validate(const char *s)
   243 }
   242 }
   244 
   243 
   245 
   244 
   246 static void EmitSingleChar(char *buf, int value)
   245 static void EmitSingleChar(char *buf, int value)
   247 {
   246 {
   248 	if (*buf != '\0') warning("Ignoring trailing letters in command");
   247 	if (*buf != '\0') strgen_warning("Ignoring trailing letters in command");
   249 	PutUtf8(value);
   248 	PutUtf8(value);
   250 }
   249 }
   251 
   250 
   252 
   251 
   253 static void EmitSetX(char *buf, int value)
   252 static void EmitSetX(char *buf, int value)
   254 {
   253 {
   255 	char *err;
   254 	char *err;
   256 	int x = strtol(buf, &err, 0);
   255 	int x = strtol(buf, &err, 0);
   257 	if (*err != 0) fatal("SetX param invalid");
   256 	if (*err != 0) error("SetX param invalid");
   258 	PutUtf8(SCC_SETX);
   257 	PutUtf8(SCC_SETX);
   259 	PutByte((byte)x);
   258 	PutByte((byte)x);
   260 }
   259 }
   261 
   260 
   262 
   261 
   265 	char *err;
   264 	char *err;
   266 	int x;
   265 	int x;
   267 	int y;
   266 	int y;
   268 
   267 
   269 	x = strtol(buf, &err, 0);
   268 	x = strtol(buf, &err, 0);
   270 	if (*err != ' ') fatal("SetXY param invalid");
   269 	if (*err != ' ') error("SetXY param invalid");
   271 	y = strtol(err + 1, &err, 0);
   270 	y = strtol(err + 1, &err, 0);
   272 	if (*err != 0) fatal("SetXY param invalid");
   271 	if (*err != 0) error("SetXY param invalid");
   273 
   272 
   274 	PutUtf8(SCC_SETXY);
   273 	PutUtf8(SCC_SETXY);
   275 	PutByte((byte)x);
   274 	PutByte((byte)x);
   276 	PutByte((byte)y);
   275 	PutByte((byte)y);
   277 }
   276 }
   369 		words[nw] = ParseWord(&buf);
   368 		words[nw] = ParseWord(&buf);
   370 		if (words[nw] == NULL) break;
   369 		if (words[nw] == NULL) break;
   371 	}
   370 	}
   372 
   371 
   373 	if (nw == 0)
   372 	if (nw == 0)
   374 		fatal("%s: No plural words", _cur_ident);
   373 		error("%s: No plural words", _cur_ident);
   375 
   374 
   376 	if (_plural_form_counts[_lang_pluralform] != nw) {
   375 	if (_plural_form_counts[_lang_pluralform] != nw) {
   377 		if (_translated) {
   376 		if (_translated) {
   378 			fatal("%s: Invalid number of plural forms. Expecting %d, found %d.", _cur_ident,
   377 			error("%s: Invalid number of plural forms. Expecting %d, found %d.", _cur_ident,
   379 				_plural_form_counts[_lang_pluralform], nw);
   378 				_plural_form_counts[_lang_pluralform], nw);
   380 		} else {
   379 		} else {
   381 			if ((_show_todo & 2) != 0) warning("'%s' is untranslated. Tweaking english string to allow compilation for plural forms", _cur_ident);
   380 			if ((_show_todo & 2) != 0) strgen_warning("'%s' is untranslated. Tweaking english string to allow compilation for plural forms", _cur_ident);
   382 			if (nw > _plural_form_counts[_lang_pluralform]) {
   381 			if (nw > _plural_form_counts[_lang_pluralform]) {
   383 				nw = _plural_form_counts[_lang_pluralform];
   382 				nw = _plural_form_counts[_lang_pluralform];
   384 			} else {
   383 			} else {
   385 				for (; nw < _plural_form_counts[_lang_pluralform]; nw++) {
   384 				for (; nw < _plural_form_counts[_lang_pluralform]; nw++) {
   386 					words[nw] = words[nw - 1];
   385 					words[nw] = words[nw - 1];
   403 	if (buf[0] == '=') {
   402 	if (buf[0] == '=') {
   404 		buf++;
   403 		buf++;
   405 
   404 
   406 		// This is a {G=DER} command
   405 		// This is a {G=DER} command
   407 		for (nw = 0; ; nw++) {
   406 		for (nw = 0; ; nw++) {
   408 			if (nw >= 8) fatal("G argument '%s' invalid", buf);
   407 			if (nw >= 8) error("G argument '%s' invalid", buf);
   409 			if (strcmp(buf, _genders[nw]) == 0) break;
   408 			if (strcmp(buf, _genders[nw]) == 0) break;
   410 		}
   409 		}
   411 		// now nw contains the gender index
   410 		// now nw contains the gender index
   412 		PutUtf8(SCC_GENDER_INDEX);
   411 		PutUtf8(SCC_GENDER_INDEX);
   413 		PutByte(nw);
   412 		PutByte(nw);
   420 
   419 
   421 		for (nw = 0; nw < 8; nw++) {
   420 		for (nw = 0; nw < 8; nw++) {
   422 			words[nw] = ParseWord(&buf);
   421 			words[nw] = ParseWord(&buf);
   423 			if (words[nw] == NULL) break;
   422 			if (words[nw] == NULL) break;
   424 		}
   423 		}
   425 		if (nw != _numgenders) fatal("Bad # of arguments for gender command");
   424 		if (nw != _numgenders) error("Bad # of arguments for gender command");
   426 		PutUtf8(SCC_GENDER_LIST);
   425 		PutUtf8(SCC_GENDER_LIST);
   427 		PutByte(TranslateArgumentIdx(argidx));
   426 		PutByte(TranslateArgumentIdx(argidx));
   428 		EmitWordList(words, nw);
   427 		EmitWordList(words, nw);
   429 	}
   428 	}
   430 }
   429 }
   551 	uint i;
   550 	uint i;
   552 
   551 
   553 	for (i = 0; i < MAX_NUM_CASES; i++) {
   552 	for (i = 0; i < MAX_NUM_CASES; i++) {
   554 		if (memcmp(_cases[i], str, len) == 0 && _cases[i][len] == 0) return i + 1;
   553 		if (memcmp(_cases[i], str, len) == 0 && _cases[i][len] == 0) return i + 1;
   555 	}
   554 	}
   556 	fatal("Invalid case-name '%s'", str);
   555 	error("Invalid case-name '%s'", str);
   557 }
   556 }
   558 
   557 
   559 
   558 
   560 // returns NULL on eof
   559 // returns NULL on eof
   561 // else returns command struct
   560 // else returns command struct
   576 
   575 
   577 	if (*s >= '0' && *s <= '9') {
   576 	if (*s >= '0' && *s <= '9') {
   578 		char *end;
   577 		char *end;
   579 
   578 
   580 		*argno = strtoul(s, &end, 0);
   579 		*argno = strtoul(s, &end, 0);
   581 		if (*end != ':') fatal("missing arg #");
   580 		if (*end != ':') error("missing arg #");
   582 		s = end + 1;
   581 		s = end + 1;
   583 	}
   582 	}
   584 
   583 
   585 	// parse command name
   584 	// parse command name
   586 	start = s;
   585 	start = s;
   588 		c = *s++;
   587 		c = *s++;
   589 	} while (c != '}' && c != ' ' && c != '=' && c != '.' && c != 0);
   588 	} while (c != '}' && c != ' ' && c != '=' && c != '.' && c != 0);
   590 
   589 
   591 	cmd = FindCmd(start, s - start - 1);
   590 	cmd = FindCmd(start, s - start - 1);
   592 	if (cmd == NULL) {
   591 	if (cmd == NULL) {
   593 		error("Undefined command '%.*s'", s - start - 1, start);
   592 		strgen_error("Undefined command '%.*s'", s - start - 1, start);
   594 		return NULL;
   593 		return NULL;
   595 	}
   594 	}
   596 
   595 
   597 	if (c == '.') {
   596 	if (c == '.') {
   598 		const char *casep = s;
   597 		const char *casep = s;
   599 
   598 
   600 		if (!(cmd->flags & C_CASE))
   599 		if (!(cmd->flags & C_CASE))
   601 			fatal("Command '%s' can't have a case", cmd->cmd);
   600 			error("Command '%s' can't have a case", cmd->cmd);
   602 
   601 
   603 		do c = *s++; while (c != '}' && c != ' ' && c != '\0');
   602 		do c = *s++; while (c != '}' && c != ' ' && c != '\0');
   604 		*casei = ResolveCaseName(casep, s - casep - 1);
   603 		*casei = ResolveCaseName(casep, s - casep - 1);
   605 	}
   604 	}
   606 
   605 
   607 	if (c == '\0') {
   606 	if (c == '\0') {
   608 		error("Missing } from command '%s'", start);
   607 		strgen_error("Missing } from command '%s'", start);
   609 		return NULL;
   608 		return NULL;
   610 	}
   609 	}
   611 
   610 
   612 
   611 
   613 	if (c != '}') {
   612 	if (c != '}') {
   616 		start = s;
   615 		start = s;
   617 		for (;;) {
   616 		for (;;) {
   618 			c = *s++;
   617 			c = *s++;
   619 			if (c == '}') break;
   618 			if (c == '}') break;
   620 			if (c == '\0') {
   619 			if (c == '\0') {
   621 				error("Missing } from command '%s'", start);
   620 				strgen_error("Missing } from command '%s'", start);
   622 				return NULL;
   621 				return NULL;
   623 			}
   622 			}
   624 			if (s - start == 250) fatal("param command too long");
   623 			if (s - start == 250) error("param command too long");
   625 			*param++ = c;
   624 			*param++ = c;
   626 		}
   625 		}
   627 	}
   626 	}
   628 	*param = '\0';
   627 	*param = '\0';
   629 
   628 
   644 	} else if (!memcmp(str, "isocode ", 8)) {
   643 	} else if (!memcmp(str, "isocode ", 8)) {
   645 		ttd_strlcpy(_lang_isocode, str + 8, sizeof(_lang_isocode));
   644 		ttd_strlcpy(_lang_isocode, str + 8, sizeof(_lang_isocode));
   646 	} else if (!memcmp(str, "plural ", 7)) {
   645 	} else if (!memcmp(str, "plural ", 7)) {
   647 		_lang_pluralform = atoi(str + 7);
   646 		_lang_pluralform = atoi(str + 7);
   648 		if (_lang_pluralform >= lengthof(_plural_form_counts))
   647 		if (_lang_pluralform >= lengthof(_plural_form_counts))
   649 			fatal("Invalid pluralform %d", _lang_pluralform);
   648 			error("Invalid pluralform %d", _lang_pluralform);
   650 	} else if (!memcmp(str, "gender ", 7)) {
   649 	} else if (!memcmp(str, "gender ", 7)) {
   651 		char* buf = str + 7;
   650 		char* buf = str + 7;
   652 
   651 
   653 		for (;;) {
   652 		for (;;) {
   654 			const char* s = ParseWord(&buf);
   653 			const char* s = ParseWord(&buf);
   655 
   654 
   656 			if (s == NULL) break;
   655 			if (s == NULL) break;
   657 			if (_numgenders >= MAX_NUM_GENDER) fatal("Too many genders, max %d", MAX_NUM_GENDER);
   656 			if (_numgenders >= MAX_NUM_GENDER) error("Too many genders, max %d", MAX_NUM_GENDER);
   658 			ttd_strlcpy(_genders[_numgenders], s, sizeof(_genders[_numgenders]));
   657 			ttd_strlcpy(_genders[_numgenders], s, sizeof(_genders[_numgenders]));
   659 			_numgenders++;
   658 			_numgenders++;
   660 		}
   659 		}
   661 	} else if (!memcmp(str, "case ", 5)) {
   660 	} else if (!memcmp(str, "case ", 5)) {
   662 		char* buf = str + 5;
   661 		char* buf = str + 5;
   663 
   662 
   664 		for (;;) {
   663 		for (;;) {
   665 			const char* s = ParseWord(&buf);
   664 			const char* s = ParseWord(&buf);
   666 
   665 
   667 			if (s == NULL) break;
   666 			if (s == NULL) break;
   668 			if (_numcases >= MAX_NUM_CASES) fatal("Too many cases, max %d", MAX_NUM_CASES);
   667 			if (_numcases >= MAX_NUM_CASES) error("Too many cases, max %d", MAX_NUM_CASES);
   669 			ttd_strlcpy(_cases[_numcases], s, sizeof(_cases[_numcases]));
   668 			ttd_strlcpy(_cases[_numcases], s, sizeof(_cases[_numcases]));
   670 			_numcases++;
   669 			_numcases++;
   671 		}
   670 		}
   672 	} else {
   671 	} else {
   673 		fatal("unknown pragma '%s'", str);
   672 		error("unknown pragma '%s'", str);
   674 	}
   673 	}
   675 }
   674 }
   676 
   675 
   677 static void ExtractCommandString(ParsedCommandStruct* p, const char* s, bool warnings)
   676 static void ExtractCommandString(ParsedCommandStruct* p, const char* s, bool warnings)
   678 {
   677 {
   688 		const CmdStruct* ar = ParseCommandString(&s, param, &argno, &casei);
   687 		const CmdStruct* ar = ParseCommandString(&s, param, &argno, &casei);
   689 
   688 
   690 		if (ar == NULL) break;
   689 		if (ar == NULL) break;
   691 
   690 
   692 		// Sanity checking
   691 		// Sanity checking
   693 		if (argno != -1 && ar->consumes == 0) fatal("Non consumer param can't have a paramindex");
   692 		if (argno != -1 && ar->consumes == 0) error("Non consumer param can't have a paramindex");
   694 
   693 
   695 		if (ar->consumes) {
   694 		if (ar->consumes) {
   696 			if (argno != -1) argidx = argno;
   695 			if (argno != -1) argidx = argno;
   697 			if (argidx < 0 || argidx >= lengthof(p->cmd)) fatal("invalid param idx %d", argidx);
   696 			if (argidx < 0 || argidx >= lengthof(p->cmd)) error("invalid param idx %d", argidx);
   698 			if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) fatal("duplicate param idx %d", argidx);
   697 			if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) error("duplicate param idx %d", argidx);
   699 
   698 
   700 			p->cmd[argidx++] = ar;
   699 			p->cmd[argidx++] = ar;
   701 		} else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them
   700 		} else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them
   702 			if (p->np >= lengthof(p->pairs)) fatal("too many commands in string, max %d", lengthof(p->pairs));
   701 			if (p->np >= lengthof(p->pairs)) error("too many commands in string, max %d", lengthof(p->pairs));
   703 			p->pairs[p->np].a = ar;
   702 			p->pairs[p->np].a = ar;
   704 			p->pairs[p->np].v = param[0] != '\0' ? strdup(param) : "";
   703 			p->pairs[p->np].v = param[0] != '\0' ? strdup(param) : "";
   705 			p->np++;
   704 			p->np++;
   706 		}
   705 		}
   707 	}
   706 	}
   736 	ExtractCommandString(&templ, b, true);
   735 	ExtractCommandString(&templ, b, true);
   737 	ExtractCommandString(&lang, a, true);
   736 	ExtractCommandString(&lang, a, true);
   738 
   737 
   739 	// For each string in templ, see if we find it in lang
   738 	// For each string in templ, see if we find it in lang
   740 	if (templ.np != lang.np) {
   739 	if (templ.np != lang.np) {
   741 		warning("%s: template string and language string have a different # of commands", name);
   740 		strgen_warning("%s: template string and language string have a different # of commands", name);
   742 		result = false;
   741 		result = false;
   743 	}
   742 	}
   744 
   743 
   745 	for (i = 0; i < templ.np; i++) {
   744 	for (i = 0; i < templ.np; i++) {
   746 		// see if we find it in lang, and zero it out
   745 		// see if we find it in lang, and zero it out
   754 				break;
   753 				break;
   755 			}
   754 			}
   756 		}
   755 		}
   757 
   756 
   758 		if (!found) {
   757 		if (!found) {
   759 			warning("%s: command '%s' exists in template file but not in language file", name, templ.pairs[i].a->cmd);
   758 			strgen_warning("%s: command '%s' exists in template file but not in language file", name, templ.pairs[i].a->cmd);
   760 			result = false;
   759 			result = false;
   761 		}
   760 		}
   762 	}
   761 	}
   763 
   762 
   764 	// if we reach here, all non consumer commands match up.
   763 	// if we reach here, all non consumer commands match up.
   765 	// Check if the non consumer commands match up also.
   764 	// Check if the non consumer commands match up also.
   766 	for (i = 0; i < lengthof(templ.cmd); i++) {
   765 	for (i = 0; i < lengthof(templ.cmd); i++) {
   767 		if (TranslateCmdForCompare(templ.cmd[i]) != TranslateCmdForCompare(lang.cmd[i])) {
   766 		if (TranslateCmdForCompare(templ.cmd[i]) != TranslateCmdForCompare(lang.cmd[i])) {
   768 			warning("%s: Param idx #%d '%s' doesn't match with template command '%s'", name, i,
   767 			strgen_warning("%s: Param idx #%d '%s' doesn't match with template command '%s'", name, i,
   769 				lang.cmd[i]  == NULL ? "<empty>" : lang.cmd[i]->cmd,
   768 				lang.cmd[i]  == NULL ? "<empty>" : lang.cmd[i]->cmd,
   770 				templ.cmd[i] == NULL ? "<empty>" : templ.cmd[i]->cmd);
   769 				templ.cmd[i] == NULL ? "<empty>" : templ.cmd[i]->cmd);
   771 			result = false;
   770 			result = false;
   772 		}
   771 		}
   773 	}
   772 	}
   789 	// Ignore comments & blank lines
   788 	// Ignore comments & blank lines
   790 	if (*str == ';' || *str == ' ' || *str == '\0') return;
   789 	if (*str == ';' || *str == ' ' || *str == '\0') return;
   791 
   790 
   792 	s = strchr(str, ':');
   791 	s = strchr(str, ':');
   793 	if (s == NULL) {
   792 	if (s == NULL) {
   794 		error("Line has no ':' delimiter");
   793 		strgen_error("Line has no ':' delimiter");
   795 		return;
   794 		return;
   796 	}
   795 	}
   797 
   796 
   798 	// Trim spaces.
   797 	// Trim spaces.
   799 	// After this str points to the command name, and s points to the command contents
   798 	// After this str points to the command name, and s points to the command contents
   804 	/* Check string is valid UTF-8 */
   803 	/* Check string is valid UTF-8 */
   805 	{
   804 	{
   806 		const char *tmp;
   805 		const char *tmp;
   807 		for (tmp = s; *tmp != '\0';) {
   806 		for (tmp = s; *tmp != '\0';) {
   808 			size_t len = Utf8Validate(tmp);
   807 			size_t len = Utf8Validate(tmp);
   809 			if (len == 0) fatal("Invalid UTF-8 sequence in '%s'", s);
   808 			if (len == 0) error("Invalid UTF-8 sequence in '%s'", s);
   810 			tmp += len;
   809 			tmp += len;
   811 		}
   810 		}
   812 	}
   811 	}
   813 
   812 
   814 	// Check if the string has a case..
   813 	// Check if the string has a case..
   819 	// Check if this string already exists..
   818 	// Check if this string already exists..
   820 	ent = HashFind(str);
   819 	ent = HashFind(str);
   821 
   820 
   822 	if (master) {
   821 	if (master) {
   823 		if (ent != NULL && casep == NULL) {
   822 		if (ent != NULL && casep == NULL) {
   824 			error("String name '%s' is used multiple times", str);
   823 			strgen_error("String name '%s' is used multiple times", str);
   825 			return;
   824 			return;
   826 		}
   825 		}
   827 
   826 
   828 		if (ent == NULL && casep != NULL) {
   827 		if (ent == NULL && casep != NULL) {
   829 			error("Base string name '%s' doesn't exist yet. Define it before defining a case.", str);
   828 			strgen_error("Base string name '%s' doesn't exist yet. Define it before defining a case.", str);
   830 			return;
   829 			return;
   831 		}
   830 		}
   832 
   831 
   833 		if (ent == NULL) {
   832 		if (ent == NULL) {
   834 			if (_strings[_next_string_id]) {
   833 			if (_strings[_next_string_id]) {
   835 				error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strings[_next_string_id]->name);
   834 				strgen_error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strings[_next_string_id]->name);
   836 				return;
   835 				return;
   837 			}
   836 			}
   838 
   837 
   839 			// Allocate a new LangString
   838 			// Allocate a new LangString
   840 			ent = CallocT<LangString>(1);
   839 			ent = CallocT<LangString>(1);
   857 			ent->english = strdup(s);
   856 			ent->english = strdup(s);
   858 		}
   857 		}
   859 
   858 
   860 	} else {
   859 	} else {
   861 		if (ent == NULL) {
   860 		if (ent == NULL) {
   862 			warning("String name '%s' does not exist in master file", str);
   861 			strgen_warning("String name '%s' does not exist in master file", str);
   863 			return;
   862 			return;
   864 		}
   863 		}
   865 
   864 
   866 		if (ent->translated && casep == NULL) {
   865 		if (ent->translated && casep == NULL) {
   867 			error("String name '%s' is used multiple times", str);
   866 			strgen_error("String name '%s' is used multiple times", str);
   868 			return;
   867 			return;
   869 		}
   868 		}
   870 
   869 
   871 		if (s[0] == ':' && s[1] == '\0' && casep == NULL) {
   870 		if (s[0] == ':' && s[1] == '\0' && casep == NULL) {
   872 			// Special syntax :: means we should just inherit the master string
   871 			// Special syntax :: means we should just inherit the master string
   910 	_lang_name[0] = _lang_ownname[0] = _lang_isocode[0] = '\0';
   909 	_lang_name[0] = _lang_ownname[0] = _lang_isocode[0] = '\0';
   911 	// TODO:!! We can't reset the cases. In case the translated strings
   910 	// TODO:!! We can't reset the cases. In case the translated strings
   912 	// derive some strings from english....
   911 	// derive some strings from english....
   913 
   912 
   914 	in = fopen(file, "r");
   913 	in = fopen(file, "r");
   915 	if (in == NULL) fatal("Cannot open file");
   914 	if (in == NULL) error("Cannot open file");
   916 	_cur_line = 1;
   915 	_cur_line = 1;
   917 	while (fgets(buf, sizeof(buf), in) != NULL) {
   916 	while (fgets(buf, sizeof(buf), in) != NULL) {
   918 		rstrip(buf);
   917 		rstrip(buf);
   919 		HandleString(buf, english);
   918 		HandleString(buf, english);
   920 		_cur_line++;
   919 		_cur_line++;
   921 	}
   920 	}
   922 	fclose(in);
   921 	fclose(in);
   923 
   922 
   924 	if (StrEmpty(_lang_name) || StrEmpty(_lang_ownname) || StrEmpty(_lang_isocode)) {
   923 	if (StrEmpty(_lang_name) || StrEmpty(_lang_ownname) || StrEmpty(_lang_isocode)) {
   925 		fatal("Language must include ##name, ##ownname and ##isocode");
   924 		error("Language must include ##name, ##ownname and ##isocode");
   926 	}
   925 	}
   927 }
   926 }
   928 
   927 
   929 
   928 
   930 static uint32 MyHashStr(uint32 hash, const char *s)
   929 static uint32 MyHashStr(uint32 hash, const char *s)
   989 
   988 
   990 	f2 = fopen(n2, "rb");
   989 	f2 = fopen(n2, "rb");
   991 	if (f2 == NULL) return false;
   990 	if (f2 == NULL) return false;
   992 
   991 
   993 	f1 = fopen(n1, "rb");
   992 	f1 = fopen(n1, "rb");
   994 	if (f1 == NULL) fatal("can't open %s", n1);
   993 	if (f1 == NULL) error("can't open %s", n1);
   995 
   994 
   996 	do {
   995 	do {
   997 		l1 = fread(b1, 1, sizeof(b1), f1);
   996 		l1 = fread(b1, 1, sizeof(b1), f1);
   998 		l2 = fread(b2, 1, sizeof(b2), f2);
   997 		l2 = fread(b2, 1, sizeof(b2), f2);
   999 
   998 
  1015 	FILE *out;
  1014 	FILE *out;
  1016 	int i;
  1015 	int i;
  1017 	int next = -1;
  1016 	int next = -1;
  1018 
  1017 
  1019 	out = fopen("tmp.xxx", "w");
  1018 	out = fopen("tmp.xxx", "w");
  1020 	if (out == NULL) fatal("can't open tmp.xxx");
  1019 	if (out == NULL) error("can't open tmp.xxx");
  1021 
  1020 
  1022 	fprintf(out, "/* This file is automatically generated. Do not modify */\n\n");
  1021 	fprintf(out, "/* This file is automatically generated. Do not modify */\n\n");
  1023 	fprintf(out, "#ifndef TABLE_STRINGS_H\n");
  1022 	fprintf(out, "#ifndef TABLE_STRINGS_H\n");
  1024 	fprintf(out, "#define TABLE_STRINGS_H\n");
  1023 	fprintf(out, "#define TABLE_STRINGS_H\n");
  1025 
  1024 
  1050 	} else {
  1049 	} else {
  1051 		// else rename tmp.xxx into filename
  1050 		// else rename tmp.xxx into filename
  1052 #if defined(WIN32) || defined(WIN64)
  1051 #if defined(WIN32) || defined(WIN64)
  1053 		unlink(filename);
  1052 		unlink(filename);
  1054 #endif
  1053 #endif
  1055 		if (rename("tmp.xxx", filename) == -1) fatal("rename() failed");
  1054 		if (rename("tmp.xxx", filename) == -1) error("rename() failed");
  1056 	}
  1055 	}
  1057 }
  1056 }
  1058 
  1057 
  1059 static int TranslateArgumentIdx(int argidx)
  1058 static int TranslateArgumentIdx(int argidx)
  1060 {
  1059 {
  1061 	int i, sum;
  1060 	int i, sum;
  1062 
  1061 
  1063 	if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
  1062 	if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
  1064 		fatal("invalid argidx %d", argidx);
  1063 		error("invalid argidx %d", argidx);
  1065 
  1064 
  1066 	for (i = sum = 0; i < argidx; i++) {
  1065 	for (i = sum = 0; i < argidx; i++) {
  1067 		const CmdStruct *cs = _cur_pcs.cmd[i];
  1066 		const CmdStruct *cs = _cur_pcs.cmd[i];
  1068 		sum += (cs != NULL) ? cs->consumes : 1;
  1067 		sum += (cs != NULL) ? cs->consumes : 1;
  1069 	}
  1068 	}
  1110 			}
  1109 			}
  1111 
  1110 
  1112 			// Output the one from the master string... it's always accurate.
  1111 			// Output the one from the master string... it's always accurate.
  1113 			cs = _cur_pcs.cmd[_cur_argidx++];
  1112 			cs = _cur_pcs.cmd[_cur_argidx++];
  1114 			if (cs == NULL) {
  1113 			if (cs == NULL) {
  1115 				fatal("%s: No argument exists at position %d", _cur_ident, _cur_argidx - 1);
  1114 				error("%s: No argument exists at position %d", _cur_ident, _cur_argidx - 1);
  1116 			}
  1115 			}
  1117 		}
  1116 		}
  1118 
  1117 
  1119 		cs->proc(param, cs->value);
  1118 		cs->proc(param, cs->value);
  1120 	}
  1119 	}
  1126 		fputc(length, f);
  1125 		fputc(length, f);
  1127 	} else if (length < 0x4000) {
  1126 	} else if (length < 0x4000) {
  1128 		fputc((length >> 8) | 0xC0, f);
  1127 		fputc((length >> 8) | 0xC0, f);
  1129 		fputc(length & 0xFF, f);
  1128 		fputc(length & 0xFF, f);
  1130 	} else {
  1129 	} else {
  1131 		fatal("string too long");
  1130 		error("string too long");
  1132 	}
  1131 	}
  1133 }
  1132 }
  1134 
  1133 
  1135 
  1134 
  1136 static void WriteLangfile(const char *filename)
  1135 static void WriteLangfile(const char *filename)
  1140 	LanguagePackHeader hdr;
  1139 	LanguagePackHeader hdr;
  1141 	uint i;
  1140 	uint i;
  1142 	uint j;
  1141 	uint j;
  1143 
  1142 
  1144 	f = fopen(filename, "wb");
  1143 	f = fopen(filename, "wb");
  1145 	if (f == NULL) fatal("can't open %s", filename);
  1144 	if (f == NULL) error("can't open %s", filename);
  1146 
  1145 
  1147 	memset(&hdr, 0, sizeof(hdr));
  1146 	memset(&hdr, 0, sizeof(hdr));
  1148 	for (i = 0; i != 32; i++) {
  1147 	for (i = 0; i != 32; i++) {
  1149 		uint n = CountInUse(i);
  1148 		uint n = CountInUse(i);
  1150 
  1149 
  1178 			_cur_line = ls->line;
  1177 			_cur_line = ls->line;
  1179 
  1178 
  1180 			// Produce a message if a string doesn't have a translation.
  1179 			// Produce a message if a string doesn't have a translation.
  1181 			if (_show_todo > 0 && ls->translated == NULL) {
  1180 			if (_show_todo > 0 && ls->translated == NULL) {
  1182 				if ((_show_todo & 2) != 0) {
  1181 				if ((_show_todo & 2) != 0) {
  1183 					warning("'%s' is untranslated", ls->name);
  1182 					strgen_warning("'%s' is untranslated", ls->name);
  1184 				}
  1183 				}
  1185 				if ((_show_todo & 1) != 0) {
  1184 				if ((_show_todo & 1) != 0) {
  1186 					const char *s = "<TODO> ";
  1185 					const char *s = "<TODO> ";
  1187 					while (*s != '\0') PutByte(*s++);
  1186 					while (*s != '\0') PutByte(*s++);
  1188 				}
  1187 				}