strgen/strgen.c
changeset 4922 dec2c076d5a7
parent 4464 08e89a8faa09
child 5108 aeaef6fe53b7
equal deleted inserted replaced
4921:b6f08b37fe62 4922:dec2c076d5a7
   156 # define LINE_NUM_FMT "(%d)"
   156 # define LINE_NUM_FMT "(%d)"
   157 #else
   157 #else
   158 # define LINE_NUM_FMT ":%d"
   158 # define LINE_NUM_FMT ":%d"
   159 #endif
   159 #endif
   160 
   160 
   161 static void CDECL Warning(const char *s, ...)
   161 static void CDECL warning(const char *s, ...)
   162 {
   162 {
   163 	char buf[1024];
   163 	char buf[1024];
   164 	va_list va;
   164 	va_list va;
   165 	va_start(va, s);
   165 	va_start(va, s);
   166 	vsnprintf(buf, lengthof(buf), s, va);
   166 	vsnprintf(buf, lengthof(buf), s, va);
   167 	va_end(va);
   167 	va_end(va);
   168 	fprintf(stderr, "%s" LINE_NUM_FMT ": warning: %s\n", _file, _cur_line, buf);
   168 	fprintf(stderr, "%s" LINE_NUM_FMT ": warning: %s\n", _file, _cur_line, buf);
   169 	_warnings++;
   169 	_warnings++;
   170 }
   170 }
   171 
   171 
   172 
   172 void CDECL error(const char *s, ...)
   173 static void CDECL Error(const char *s, ...)
       
   174 {
   173 {
   175 	char buf[1024];
   174 	char buf[1024];
   176 	va_list va;
   175 	va_list va;
   177 	va_start(va, s);
   176 	va_start(va, s);
   178 	vsnprintf(buf, lengthof(buf), s, va);
   177 	vsnprintf(buf, lengthof(buf), s, va);
   180 	fprintf(stderr, "%s" LINE_NUM_FMT ": error: %s\n", _file, _cur_line, buf);
   179 	fprintf(stderr, "%s" LINE_NUM_FMT ": error: %s\n", _file, _cur_line, buf);
   181 	_errors++;
   180 	_errors++;
   182 }
   181 }
   183 
   182 
   184 
   183 
   185 static void NORETURN CDECL Fatal(const char *s, ...)
   184 static void NORETURN CDECL fatal(const char *s, ...)
   186 {
   185 {
   187 	char buf[1024];
   186 	char buf[1024];
   188 	va_list va;
   187 	va_list va;
   189 	va_start(va, s);
   188 	va_start(va, s);
   190 	vsnprintf(buf, lengthof(buf), s, va);
   189 	vsnprintf(buf, lengthof(buf), s, va);
   193 	exit(1);
   192 	exit(1);
   194 }
   193 }
   195 
   194 
   196 static void PutByte(byte c)
   195 static void PutByte(byte c)
   197 {
   196 {
   198 	if (_put_pos == lengthof(_put_buf)) Fatal("Put buffer too small");
   197 	if (_put_pos == lengthof(_put_buf)) fatal("Put buffer too small");
   199 	_put_buf[_put_pos++] = c;
   198 	_put_buf[_put_pos++] = c;
   200 }
   199 }
   201 
   200 
   202 
   201 
   203 static void EmitSingleByte(char *buf, int value)
   202 static void EmitSingleByte(char *buf, int value)
   204 {
   203 {
   205 	if (*buf != '\0') Warning("Ignoring trailing letters in command");
   204 	if (*buf != '\0') warning("Ignoring trailing letters in command");
   206 	PutByte((byte)value);
   205 	PutByte((byte)value);
   207 }
   206 }
   208 
   207 
   209 
   208 
   210 static void EmitEscapedByte(char *buf, int value)
   209 static void EmitEscapedByte(char *buf, int value)
   211 {
   210 {
   212 	if (*buf != '\0') Warning("Ignoring trailing letters in command");
   211 	if (*buf != '\0') warning("Ignoring trailing letters in command");
   213 	PutByte(0x85);
   212 	PutByte(0x85);
   214 	PutByte((byte)value);
   213 	PutByte((byte)value);
   215 }
   214 }
   216 
   215 
   217 static void EmitSetX(char *buf, int value)
   216 static void EmitSetX(char *buf, int value)
   218 {
   217 {
   219 	char *err;
   218 	char *err;
   220 	int x = strtol(buf, &err, 0);
   219 	int x = strtol(buf, &err, 0);
   221 	if (*err != 0) Fatal("SetX param invalid");
   220 	if (*err != 0) fatal("SetX param invalid");
   222 	PutByte(1);
   221 	PutByte(1);
   223 	PutByte((byte)x);
   222 	PutByte((byte)x);
   224 }
   223 }
   225 
   224 
   226 
   225 
   229 	char *err;
   228 	char *err;
   230 	int x;
   229 	int x;
   231 	int y;
   230 	int y;
   232 
   231 
   233 	x = strtol(buf, &err, 0);
   232 	x = strtol(buf, &err, 0);
   234 	if (*err != ' ') Fatal("SetXY param invalid");
   233 	if (*err != ' ') fatal("SetXY param invalid");
   235 	y = strtol(err + 1, &err, 0);
   234 	y = strtol(err + 1, &err, 0);
   236 	if (*err != 0) Fatal("SetXY param invalid");
   235 	if (*err != 0) fatal("SetXY param invalid");
   237 
   236 
   238 	PutByte(2);
   237 	PutByte(2);
   239 	PutByte((byte)x);
   238 	PutByte((byte)x);
   240 	PutByte((byte)y);
   239 	PutByte((byte)y);
   241 }
   240 }
   333 		words[nw] = ParseWord(&buf);
   332 		words[nw] = ParseWord(&buf);
   334 		if (words[nw] == NULL) break;
   333 		if (words[nw] == NULL) break;
   335 	}
   334 	}
   336 
   335 
   337 	if (nw == 0)
   336 	if (nw == 0)
   338 		Fatal("%s: No plural words", _cur_ident);
   337 		fatal("%s: No plural words", _cur_ident);
   339 
   338 
   340 	if (_plural_form_counts[_lang_pluralform] != nw) {
   339 	if (_plural_form_counts[_lang_pluralform] != nw) {
   341 		if (_translated) {
   340 		if (_translated) {
   342 			Fatal("%s: Invalid number of plural forms. Expecting %d, found %d.", _cur_ident,
   341 			fatal("%s: Invalid number of plural forms. Expecting %d, found %d.", _cur_ident,
   343 				_plural_form_counts[_lang_pluralform], nw);
   342 				_plural_form_counts[_lang_pluralform], nw);
   344 		} else {
   343 		} else {
   345 			Warning("'%s' is untranslated. Tweaking english string to allow compilation for plural forms", _cur_ident);
   344 			warning("'%s' is untranslated. Tweaking english string to allow compilation for plural forms", _cur_ident);
   346 			if (nw > _plural_form_counts[_lang_pluralform]) {
   345 			if (nw > _plural_form_counts[_lang_pluralform]) {
   347 				nw = _plural_form_counts[_lang_pluralform];
   346 				nw = _plural_form_counts[_lang_pluralform];
   348 			} else {
   347 			} else {
   349 				for (; nw < _plural_form_counts[_lang_pluralform]; nw++) {
   348 				for (; nw < _plural_form_counts[_lang_pluralform]; nw++) {
   350 					words[nw] = words[nw - 1];
   349 					words[nw] = words[nw - 1];
   367 	if (buf[0] == '=') {
   366 	if (buf[0] == '=') {
   368 		buf++;
   367 		buf++;
   369 
   368 
   370 		// This is a {G=DER} command
   369 		// This is a {G=DER} command
   371 		for (nw = 0; ; nw++) {
   370 		for (nw = 0; ; nw++) {
   372 			if (nw >= 8) Fatal("G argument '%s' invalid", buf);
   371 			if (nw >= 8) fatal("G argument '%s' invalid", buf);
   373 			if (strcmp(buf, _genders[nw]) == 0) break;
   372 			if (strcmp(buf, _genders[nw]) == 0) break;
   374 		}
   373 		}
   375 		// now nw contains the gender index
   374 		// now nw contains the gender index
   376 		PutByte(0x87);
   375 		PutByte(0x87);
   377 		PutByte(nw);
   376 		PutByte(nw);
   384 
   383 
   385 		for (nw = 0; nw < 8; nw++) {
   384 		for (nw = 0; nw < 8; nw++) {
   386 			words[nw] = ParseWord(&buf);
   385 			words[nw] = ParseWord(&buf);
   387 			if (words[nw] == NULL) break;
   386 			if (words[nw] == NULL) break;
   388 		}
   387 		}
   389 		if (nw != _numgenders) Fatal("Bad # of arguments for gender command");
   388 		if (nw != _numgenders) fatal("Bad # of arguments for gender command");
   390 		PutByte(0x85);
   389 		PutByte(0x85);
   391 		PutByte(13);
   390 		PutByte(13);
   392 		PutByte(TranslateArgumentIdx(argidx));
   391 		PutByte(TranslateArgumentIdx(argidx));
   393 		EmitWordList(words, nw);
   392 		EmitWordList(words, nw);
   394 	}
   393 	}
   518 	uint i;
   517 	uint i;
   519 
   518 
   520 	for (i = 0; i < MAX_NUM_CASES; i++) {
   519 	for (i = 0; i < MAX_NUM_CASES; i++) {
   521 		if (memcmp(_cases[i], str, len) == 0 && _cases[i][len] == 0) return i + 1;
   520 		if (memcmp(_cases[i], str, len) == 0 && _cases[i][len] == 0) return i + 1;
   522 	}
   521 	}
   523 	Fatal("Invalid case-name '%s'", str);
   522 	fatal("Invalid case-name '%s'", str);
   524 }
   523 }
   525 
   524 
   526 
   525 
   527 // returns NULL on eof
   526 // returns NULL on eof
   528 // else returns command struct
   527 // else returns command struct
   543 
   542 
   544 	if (*s >= '0' && *s <= '9') {
   543 	if (*s >= '0' && *s <= '9') {
   545 		char *end;
   544 		char *end;
   546 
   545 
   547 		*argno = strtoul(s, &end, 0);
   546 		*argno = strtoul(s, &end, 0);
   548 		if (*end != ':') Fatal("missing arg #");
   547 		if (*end != ':') fatal("missing arg #");
   549 		s = end + 1;
   548 		s = end + 1;
   550 	}
   549 	}
   551 
   550 
   552 	// parse command name
   551 	// parse command name
   553 	start = s;
   552 	start = s;
   555 		c = *s++;
   554 		c = *s++;
   556 	} while (c != '}' && c != ' ' && c != '=' && c != '.' && c != 0);
   555 	} while (c != '}' && c != ' ' && c != '=' && c != '.' && c != 0);
   557 
   556 
   558 	cmd = FindCmd(start, s - start - 1);
   557 	cmd = FindCmd(start, s - start - 1);
   559 	if (cmd == NULL) {
   558 	if (cmd == NULL) {
   560 		Error("Undefined command '%.*s'", s - start - 1, start);
   559 		error("Undefined command '%.*s'", s - start - 1, start);
   561 		return NULL;
   560 		return NULL;
   562 	}
   561 	}
   563 
   562 
   564 	if (c == '.') {
   563 	if (c == '.') {
   565 		const char *casep = s;
   564 		const char *casep = s;
   566 
   565 
   567 		if (!(cmd->flags & C_CASE))
   566 		if (!(cmd->flags & C_CASE))
   568 			Fatal("Command '%s' can't have a case", cmd->cmd);
   567 			fatal("Command '%s' can't have a case", cmd->cmd);
   569 
   568 
   570 		do c = *s++; while (c != '}' && c != ' ' && c != '\0');
   569 		do c = *s++; while (c != '}' && c != ' ' && c != '\0');
   571 		*casei = ResolveCaseName(casep, s - casep - 1);
   570 		*casei = ResolveCaseName(casep, s - casep - 1);
   572 	}
   571 	}
   573 
   572 
   574 	if (c == '\0') {
   573 	if (c == '\0') {
   575 		Error("Missing } from command '%s'", start);
   574 		error("Missing } from command '%s'", start);
   576 		return NULL;
   575 		return NULL;
   577 	}
   576 	}
   578 
   577 
   579 
   578 
   580 	if (c != '}') {
   579 	if (c != '}') {
   583 		start = s;
   582 		start = s;
   584 		for (;;) {
   583 		for (;;) {
   585 			c = *s++;
   584 			c = *s++;
   586 			if (c == '}') break;
   585 			if (c == '}') break;
   587 			if (c == '\0') {
   586 			if (c == '\0') {
   588 				Error("Missing } from command '%s'", start);
   587 				error("Missing } from command '%s'", start);
   589 				return NULL;
   588 				return NULL;
   590 			}
   589 			}
   591 			if (s - start == 250) Fatal("param command too long");
   590 			if (s - start == 250) fatal("param command too long");
   592 			*param++ = c;
   591 			*param++ = c;
   593 		}
   592 		}
   594 	}
   593 	}
   595 	*param = '\0';
   594 	*param = '\0';
   596 
   595 
   611 	} else if (!memcmp(str, "isocode ", 8)) {
   610 	} else if (!memcmp(str, "isocode ", 8)) {
   612 		ttd_strlcpy(_lang_isocode, str + 8, sizeof(_lang_isocode));
   611 		ttd_strlcpy(_lang_isocode, str + 8, sizeof(_lang_isocode));
   613 	} else if (!memcmp(str, "plural ", 7)) {
   612 	} else if (!memcmp(str, "plural ", 7)) {
   614 		_lang_pluralform = atoi(str + 7);
   613 		_lang_pluralform = atoi(str + 7);
   615 		if (_lang_pluralform >= lengthof(_plural_form_counts))
   614 		if (_lang_pluralform >= lengthof(_plural_form_counts))
   616 			Fatal("Invalid pluralform %d", _lang_pluralform);
   615 			fatal("Invalid pluralform %d", _lang_pluralform);
   617 	} else if (!memcmp(str, "gender ", 7)) {
   616 	} else if (!memcmp(str, "gender ", 7)) {
   618 		char* buf = str + 7;
   617 		char* buf = str + 7;
   619 
   618 
   620 		for (;;) {
   619 		for (;;) {
   621 			const char* s = ParseWord(&buf);
   620 			const char* s = ParseWord(&buf);
   622 
   621 
   623 			if (s == NULL) break;
   622 			if (s == NULL) break;
   624 			if (_numgenders >= MAX_NUM_GENDER) Fatal("Too many genders, max %d", MAX_NUM_GENDER);
   623 			if (_numgenders >= MAX_NUM_GENDER) fatal("Too many genders, max %d", MAX_NUM_GENDER);
   625 			ttd_strlcpy(_genders[_numgenders], s, sizeof(_genders[_numgenders]));
   624 			ttd_strlcpy(_genders[_numgenders], s, sizeof(_genders[_numgenders]));
   626 			_numgenders++;
   625 			_numgenders++;
   627 		}
   626 		}
   628 	} else if (!memcmp(str, "case ", 5)) {
   627 	} else if (!memcmp(str, "case ", 5)) {
   629 		char* buf = str + 5;
   628 		char* buf = str + 5;
   630 
   629 
   631 		for (;;) {
   630 		for (;;) {
   632 			const char* s = ParseWord(&buf);
   631 			const char* s = ParseWord(&buf);
   633 
   632 
   634 			if (s == NULL) break;
   633 			if (s == NULL) break;
   635 			if (_numcases >= MAX_NUM_CASES) Fatal("Too many cases, max %d", MAX_NUM_CASES);
   634 			if (_numcases >= MAX_NUM_CASES) fatal("Too many cases, max %d", MAX_NUM_CASES);
   636 			ttd_strlcpy(_cases[_numcases], s, sizeof(_cases[_numcases]));
   635 			ttd_strlcpy(_cases[_numcases], s, sizeof(_cases[_numcases]));
   637 			_numcases++;
   636 			_numcases++;
   638 		}
   637 		}
   639 	} else {
   638 	} else {
   640 		Fatal("unknown pragma '%s'", str);
   639 		fatal("unknown pragma '%s'", str);
   641 	}
   640 	}
   642 }
   641 }
   643 
   642 
   644 static void ExtractCommandString(ParsedCommandStruct* p, const char* s, bool warnings)
   643 static void ExtractCommandString(ParsedCommandStruct* p, const char* s, bool warnings)
   645 {
   644 {
   655 		const CmdStruct* ar = ParseCommandString(&s, param, &argno, &casei);
   654 		const CmdStruct* ar = ParseCommandString(&s, param, &argno, &casei);
   656 
   655 
   657 		if (ar == NULL) break;
   656 		if (ar == NULL) break;
   658 
   657 
   659 		// Sanity checking
   658 		// Sanity checking
   660 		if (argno != -1 && ar->consumes == 0) Fatal("Non consumer param can't have a paramindex");
   659 		if (argno != -1 && ar->consumes == 0) fatal("Non consumer param can't have a paramindex");
   661 
   660 
   662 		if (ar->consumes) {
   661 		if (ar->consumes) {
   663 			if (argno != -1) argidx = argno;
   662 			if (argno != -1) argidx = argno;
   664 			if (argidx < 0 || argidx >= lengthof(p->cmd)) Fatal("invalid param idx %d", argidx);
   663 			if (argidx < 0 || argidx >= lengthof(p->cmd)) fatal("invalid param idx %d", argidx);
   665 			if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) Fatal("duplicate param idx %d", argidx);
   664 			if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) fatal("duplicate param idx %d", argidx);
   666 
   665 
   667 			p->cmd[argidx++] = ar;
   666 			p->cmd[argidx++] = ar;
   668 		} else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them
   667 		} else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them
   669 			if (p->np >= lengthof(p->pairs)) Fatal("too many commands in string, max %d", lengthof(p->pairs));
   668 			if (p->np >= lengthof(p->pairs)) fatal("too many commands in string, max %d", lengthof(p->pairs));
   670 			p->pairs[p->np].a = ar;
   669 			p->pairs[p->np].a = ar;
   671 			p->pairs[p->np].v = param[0] != '\0' ? strdup(param) : "";
   670 			p->pairs[p->np].v = param[0] != '\0' ? strdup(param) : "";
   672 			p->np++;
   671 			p->np++;
   673 		}
   672 		}
   674 	}
   673 	}
   703 	ExtractCommandString(&templ, b, true);
   702 	ExtractCommandString(&templ, b, true);
   704 	ExtractCommandString(&lang, a, true);
   703 	ExtractCommandString(&lang, a, true);
   705 
   704 
   706 	// For each string in templ, see if we find it in lang
   705 	// For each string in templ, see if we find it in lang
   707 	if (templ.np != lang.np) {
   706 	if (templ.np != lang.np) {
   708 		Warning("%s: template string and language string have a different # of commands", name);
   707 		warning("%s: template string and language string have a different # of commands", name);
   709 		result = false;
   708 		result = false;
   710 	}
   709 	}
   711 
   710 
   712 	for (i = 0; i < templ.np; i++) {
   711 	for (i = 0; i < templ.np; i++) {
   713 		// see if we find it in lang, and zero it out
   712 		// see if we find it in lang, and zero it out
   721 				break;
   720 				break;
   722 			}
   721 			}
   723 		}
   722 		}
   724 
   723 
   725 		if (!found) {
   724 		if (!found) {
   726 			Warning("%s: command '%s' exists in template file but not in language file", name, templ.pairs[i].a->cmd);
   725 			warning("%s: command '%s' exists in template file but not in language file", name, templ.pairs[i].a->cmd);
   727 			result = false;
   726 			result = false;
   728 		}
   727 		}
   729 	}
   728 	}
   730 
   729 
   731 	// if we reach here, all non consumer commands match up.
   730 	// if we reach here, all non consumer commands match up.
   732 	// Check if the non consumer commands match up also.
   731 	// Check if the non consumer commands match up also.
   733 	for (i = 0; i < lengthof(templ.cmd); i++) {
   732 	for (i = 0; i < lengthof(templ.cmd); i++) {
   734 		if (TranslateCmdForCompare(templ.cmd[i]) != TranslateCmdForCompare(lang.cmd[i])) {
   733 		if (TranslateCmdForCompare(templ.cmd[i]) != TranslateCmdForCompare(lang.cmd[i])) {
   735 			Warning("%s: Param idx #%d '%s' doesn't match with template command '%s'", name, i,
   734 			warning("%s: Param idx #%d '%s' doesn't match with template command '%s'", name, i,
   736 				lang.cmd[i]  == NULL ? "<empty>" : lang.cmd[i]->cmd,
   735 				lang.cmd[i]  == NULL ? "<empty>" : lang.cmd[i]->cmd,
   737 				templ.cmd[i] == NULL ? "<empty>" : templ.cmd[i]->cmd);
   736 				templ.cmd[i] == NULL ? "<empty>" : templ.cmd[i]->cmd);
   738 			result = false;
   737 			result = false;
   739 		}
   738 		}
   740 	}
   739 	}
   756 	// Ignore comments & blank lines
   755 	// Ignore comments & blank lines
   757 	if (*str == ';' || *str == ' ' || *str == '\0') return;
   756 	if (*str == ';' || *str == ' ' || *str == '\0') return;
   758 
   757 
   759 	s = strchr(str, ':');
   758 	s = strchr(str, ':');
   760 	if (s == NULL) {
   759 	if (s == NULL) {
   761 		Error("Line has no ':' delimiter");
   760 		error("Line has no ':' delimiter");
   762 		return;
   761 		return;
   763 	}
   762 	}
   764 
   763 
   765 	// Trim spaces.
   764 	// Trim spaces.
   766 	// After this str points to the command name, and s points to the command contents
   765 	// After this str points to the command name, and s points to the command contents
   776 	// Check if this string already exists..
   775 	// Check if this string already exists..
   777 	ent = HashFind(str);
   776 	ent = HashFind(str);
   778 
   777 
   779 	if (master) {
   778 	if (master) {
   780 		if (ent != NULL && casep == NULL) {
   779 		if (ent != NULL && casep == NULL) {
   781 			Error("String name '%s' is used multiple times", str);
   780 			error("String name '%s' is used multiple times", str);
   782 			return;
   781 			return;
   783 		}
   782 		}
   784 
   783 
   785 		if (ent == NULL && casep != NULL) {
   784 		if (ent == NULL && casep != NULL) {
   786 			Error("Base string name '%s' doesn't exist yet. Define it before defining a case.", str);
   785 			error("Base string name '%s' doesn't exist yet. Define it before defining a case.", str);
   787 			return;
   786 			return;
   788 		}
   787 		}
   789 
   788 
   790 		if (ent == NULL) {
   789 		if (ent == NULL) {
   791 			if (_strings[_next_string_id]) {
   790 			if (_strings[_next_string_id]) {
   792 				Error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strings[_next_string_id]->name);
   791 				error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strings[_next_string_id]->name);
   793 				return;
   792 				return;
   794 			}
   793 			}
   795 
   794 
   796 			// Allocate a new LangString
   795 			// Allocate a new LangString
   797 			ent = calloc(1, sizeof(*ent));
   796 			ent = calloc(1, sizeof(*ent));
   814 			ent->english = strdup(s);
   813 			ent->english = strdup(s);
   815 		}
   814 		}
   816 
   815 
   817 	} else {
   816 	} else {
   818 		if (ent == NULL) {
   817 		if (ent == NULL) {
   819 			Warning("String name '%s' does not exist in master file", str);
   818 			warning("String name '%s' does not exist in master file", str);
   820 			return;
   819 			return;
   821 		}
   820 		}
   822 
   821 
   823 		if (ent->translated && casep == NULL) {
   822 		if (ent->translated && casep == NULL) {
   824 			Error("String name '%s' is used multiple times", str);
   823 			error("String name '%s' is used multiple times", str);
   825 			return;
   824 			return;
   826 		}
   825 		}
   827 
   826 
   828 		if (s[0] == ':' && s[1] == '\0' && casep == NULL) {
   827 		if (s[0] == ':' && s[1] == '\0' && casep == NULL) {
   829 			// Special syntax :: means we should just inherit the master string
   828 			// Special syntax :: means we should just inherit the master string
   867 	// TODO:!! We can't reset the cases. In case the translated strings
   866 	// TODO:!! We can't reset the cases. In case the translated strings
   868 	// derive some strings from english....
   867 	// derive some strings from english....
   869 
   868 
   870 
   869 
   871 	in = fopen(file, "r");
   870 	in = fopen(file, "r");
   872 	if (in == NULL) Fatal("Cannot open file");
   871 	if (in == NULL) fatal("Cannot open file");
   873 	_cur_line = 1;
   872 	_cur_line = 1;
   874 	while (fgets(buf, sizeof(buf),in) != NULL) {
   873 	while (fgets(buf, sizeof(buf),in) != NULL) {
   875 		rstrip(buf);
   874 		rstrip(buf);
   876 		HandleString(buf, english);
   875 		HandleString(buf, english);
   877 		_cur_line++;
   876 		_cur_line++;
   942 
   941 
   943 	f2 = fopen(n2, "rb");
   942 	f2 = fopen(n2, "rb");
   944 	if (f2 == NULL) return false;
   943 	if (f2 == NULL) return false;
   945 
   944 
   946 	f1 = fopen(n1, "rb");
   945 	f1 = fopen(n1, "rb");
   947 	if (f1 == NULL) Fatal("can't open %s", n1);
   946 	if (f1 == NULL) fatal("can't open %s", n1);
   948 
   947 
   949 	do {
   948 	do {
   950 		l1 = fread(b1, 1, sizeof(b1), f1);
   949 		l1 = fread(b1, 1, sizeof(b1), f1);
   951 		l2 = fread(b2, 1, sizeof(b2), f2);
   950 		l2 = fread(b2, 1, sizeof(b2), f2);
   952 
   951 
   969 	int i;
   968 	int i;
   970 	int next = -1;
   969 	int next = -1;
   971 	int lastgrp;
   970 	int lastgrp;
   972 
   971 
   973 	out = fopen("tmp.xxx", "w");
   972 	out = fopen("tmp.xxx", "w");
   974 	if (out == NULL) Fatal("can't open tmp.xxx");
   973 	if (out == NULL) fatal("can't open tmp.xxx");
   975 
   974 
   976 	fprintf(out, "enum {");
   975 	fprintf(out, "enum {");
   977 
   976 
   978 	lastgrp = 0;
   977 	lastgrp = 0;
   979 
   978 
  1006 	} else {
  1005 	} else {
  1007 		// else rename tmp.xxx into filename
  1006 		// else rename tmp.xxx into filename
  1008 #if defined(WIN32) || defined(WIN64)
  1007 #if defined(WIN32) || defined(WIN64)
  1009 		unlink(filename);
  1008 		unlink(filename);
  1010 #endif
  1009 #endif
  1011 		if (rename("tmp.xxx", filename) == -1) Fatal("rename() failed");
  1010 		if (rename("tmp.xxx", filename) == -1) fatal("rename() failed");
  1012 	}
  1011 	}
  1013 }
  1012 }
  1014 
  1013 
  1015 static int TranslateArgumentIdx(int argidx)
  1014 static int TranslateArgumentIdx(int argidx)
  1016 {
  1015 {
  1017 	int i, sum;
  1016 	int i, sum;
  1018 
  1017 
  1019 	if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
  1018 	if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
  1020 		Fatal("invalid argidx %d", argidx);
  1019 		fatal("invalid argidx %d", argidx);
  1021 
  1020 
  1022 	for (i = sum = 0; i < argidx; i++) {
  1021 	for (i = sum = 0; i < argidx; i++) {
  1023 		const CmdStruct *cs = _cur_pcs.cmd[i];
  1022 		const CmdStruct *cs = _cur_pcs.cmd[i];
  1024 		sum += (cs != NULL) ? cs->consumes : 1;
  1023 		sum += (cs != NULL) ? cs->consumes : 1;
  1025 	}
  1024 	}
  1066 			}
  1065 			}
  1067 
  1066 
  1068 			// Output the one from the master string... it's always accurate.
  1067 			// Output the one from the master string... it's always accurate.
  1069 			cs = _cur_pcs.cmd[_cur_argidx++];
  1068 			cs = _cur_pcs.cmd[_cur_argidx++];
  1070 			if (cs == NULL) {
  1069 			if (cs == NULL) {
  1071 				Fatal("%s: No argument exists at position %d", _cur_ident, _cur_argidx - 1);
  1070 				fatal("%s: No argument exists at position %d", _cur_ident, _cur_argidx - 1);
  1072 			}
  1071 			}
  1073 		}
  1072 		}
  1074 
  1073 
  1075 		cs->proc(param, cs->value);
  1074 		cs->proc(param, cs->value);
  1076 	}
  1075 	}
  1082 		fputc(length, f);
  1081 		fputc(length, f);
  1083 	} else if (length < 0x4000) {
  1082 	} else if (length < 0x4000) {
  1084 		fputc((length >> 8) | 0xC0, f);
  1083 		fputc((length >> 8) | 0xC0, f);
  1085 		fputc(length & 0xFF, f);
  1084 		fputc(length & 0xFF, f);
  1086 	} else {
  1085 	} else {
  1087 		Fatal("string too long");
  1086 		fatal("string too long");
  1088 	}
  1087 	}
  1089 }
  1088 }
  1090 
  1089 
  1091 
  1090 
  1092 static void WriteLangfile(const char *filename, int show_todo)
  1091 static void WriteLangfile(const char *filename, int show_todo)
  1096 	LanguagePackHeader hdr;
  1095 	LanguagePackHeader hdr;
  1097 	uint i;
  1096 	uint i;
  1098 	uint j;
  1097 	uint j;
  1099 
  1098 
  1100 	f = fopen(filename, "wb");
  1099 	f = fopen(filename, "wb");
  1101 	if (f == NULL) Fatal("can't open %s", filename);
  1100 	if (f == NULL) fatal("can't open %s", filename);
  1102 
  1101 
  1103 	memset(&hdr, 0, sizeof(hdr));
  1102 	memset(&hdr, 0, sizeof(hdr));
  1104 	for (i = 0; i != 32; i++) {
  1103 	for (i = 0; i != 32; i++) {
  1105 		uint n = CountInUse(i);
  1104 		uint n = CountInUse(i);
  1106 
  1105 
  1134 			_cur_line = ls->line;
  1133 			_cur_line = ls->line;
  1135 
  1134 
  1136 			// Produce a message if a string doesn't have a translation.
  1135 			// Produce a message if a string doesn't have a translation.
  1137 			if (show_todo > 0 && ls->translated == NULL) {
  1136 			if (show_todo > 0 && ls->translated == NULL) {
  1138 				if (show_todo == 2) {
  1137 				if (show_todo == 2) {
  1139 					Warning("'%s' is untranslated", ls->name);
  1138 					warning("'%s' is untranslated", ls->name);
  1140 				} else {
  1139 				} else {
  1141 					const char *s = "<TODO> ";
  1140 					const char *s = "<TODO> ";
  1142 					while (*s != '\0') PutByte(*s++);
  1141 					while (*s != '\0') PutByte(*s++);
  1143 				}
  1142 				}
  1144 			}
  1143 			}