make race::_name a string, not an array of strings. add a test for rc_name.

This commit is contained in:
Enno Rehling 2014-08-24 21:49:55 +02:00
parent de5ef3f87b
commit 4c00777553
34 changed files with 459 additions and 433 deletions

View File

@ -385,7 +385,7 @@ static int tolua_faction_set_locale(lua_State * L)
static int tolua_faction_get_race(lua_State * L) static int tolua_faction_get_race(lua_State * L)
{ {
faction *self = (faction *) tolua_tousertype(L, 1, 0); faction *self = (faction *) tolua_tousertype(L, 1, 0);
tolua_pushstring(L, self->race->_name[0]); tolua_pushstring(L, self->race->_name);
return 1; return 1;
} }

View File

@ -68,7 +68,7 @@ static int fix_familiars(struct lua_State *L)
free(mage->spellbook); free(mage->spellbook);
mage->spellbook = 0; mage->spellbook = 0;
_snprintf(buffer, sizeof(buffer), "%s_familiar", u_race(u)->_name[0]); _snprintf(buffer, sizeof(buffer), "%s_familiar", u_race(u)->_name);
eq = get_equipment(buffer); eq = get_equipment(buffer);
if (eq) { if (eq) {
equip_unit_mask(u, eq, EQUIP_SPELLS); equip_unit_mask(u, eq, EQUIP_SPELLS);

View File

@ -840,7 +840,7 @@ static int tolua_unit_set_faction(lua_State * L)
static int tolua_unit_get_race(lua_State * L) static int tolua_unit_get_race(lua_State * L)
{ {
unit *self = (unit *) tolua_tousertype(L, 1, 0); unit *self = (unit *) tolua_tousertype(L, 1, 0);
tolua_pushstring(L, u_race(self)->_name[0]); tolua_pushstring(L, u_race(self)->_name);
return 1; return 1;
} }

View File

@ -711,7 +711,7 @@ static int tolua_write_spells(lua_State * L)
for (; comp->type != 0; ++comp) { for (; comp->type != 0; ++comp) {
static const char *costs[] = { "fixed", "level", "linear" }; static const char *costs[] = { "fixed", "level", "linear" };
xmlNodePtr cnode = xmlNewNode(NULL, BAD_CAST "resource"); xmlNodePtr cnode = xmlNewNode(NULL, BAD_CAST "resource");
xmlNewProp(cnode, BAD_CAST "name", BAD_CAST comp->type->_name[0]); xmlNewProp(cnode, BAD_CAST "name", BAD_CAST comp->type->_name);
xmlNewProp(cnode, BAD_CAST "amount", xml_i(comp->amount)); xmlNewProp(cnode, BAD_CAST "amount", xml_i(comp->amount));
xmlNewProp(cnode, BAD_CAST "cost", BAD_CAST costs[comp->cost]); xmlNewProp(cnode, BAD_CAST "cost", BAD_CAST costs[comp->cost]);
xmlAddChild(node, cnode); xmlAddChild(node, cnode);

View File

@ -340,7 +340,7 @@ static int cr_race(variant var, char *buffer, const void *userdata)
{ {
const faction *report = (const faction *)userdata; const faction *report = (const faction *)userdata;
const struct race *rc = (const race *)var.v; const struct race *rc = (const race *)var.v;
const char *key = rc_name(rc, 0); const char *key = rc_name(rc, NAME_SINGULAR);
sprintf(buffer, "\"%s\"", sprintf(buffer, "\"%s\"",
translate(key, locale_string(report->locale, key))); translate(key, locale_string(report->locale, key)));
return 0; return 0;
@ -832,20 +832,20 @@ static void cr_output_unit(FILE * F, const region * r, const faction * f,
if (pzTmp) { if (pzTmp) {
fprintf(F, "\"%s\";Typ\n", pzTmp); fprintf(F, "\"%s\";Typ\n", pzTmp);
if (u->faction == f && fval(u_race(u), RCF_SHAPESHIFTANY)) { if (u->faction == f && fval(u_race(u), RCF_SHAPESHIFTANY)) {
const char *zRace = rc_name(u_race(u), 1); const char *zRace = rc_name(u_race(u), NAME_PLURAL);
fprintf(F, "\"%s\";wahrerTyp\n", fprintf(F, "\"%s\";wahrerTyp\n",
translate(zRace, locale_string(f->locale, zRace))); translate(zRace, locale_string(f->locale, zRace)));
} }
} }
else { else {
const race *irace = u_irace(u); const race *irace = u_irace(u);
const char *zRace = rc_name(irace, 1); const char *zRace = rc_name(irace, NAME_PLURAL);
fprintf(F, "\"%s\";Typ\n", fprintf(F, "\"%s\";Typ\n",
translate(zRace, locale_string(f->locale, zRace))); translate(zRace, locale_string(f->locale, zRace)));
if (u->faction == f && irace != u_race(u)) { if (u->faction == f && irace != u_race(u)) {
assert(skill_enabled(SK_STEALTH) assert(skill_enabled(SK_STEALTH)
|| !"we're resetting this on load, so.. ircase should never be used"); || !"we're resetting this on load, so.. ircase should never be used");
zRace = rc_name(u_race(u), 1); zRace = rc_name(u_race(u), NAME_PLURAL);
fprintf(F, "\"%s\";wahrerTyp\n", fprintf(F, "\"%s\";wahrerTyp\n",
translate(zRace, locale_string(f->locale, zRace))); translate(zRace, locale_string(f->locale, zRace)));
} }
@ -1546,7 +1546,7 @@ report_computer(const char *filename, report_context * ctx, const char *charset)
fprintf(F, "%d;Punktedurchschnitt\n", avgscore); fprintf(F, "%d;Punktedurchschnitt\n", avgscore);
#endif #endif
{ {
const char *zRace = rc_name(f->race, 1); const char *zRace = rc_name(f->race, NAME_PLURAL);
fprintf(F, "\"%s\";Typ\n", translate(zRace, LOC(f->locale, zRace))); fprintf(F, "\"%s\";Typ\n", translate(zRace, LOC(f->locale, zRace)));
} }
prefix = get_prefix(f->attribs); prefix = get_prefix(f->attribs);

View File

@ -259,7 +259,7 @@ static void add_recruits(unit * u, int number, int wanted)
} }
strlcpy(equipment, "new_", sizeof(equipment)); strlcpy(equipment, "new_", sizeof(equipment));
strlcat(equipment, u_race(u)->_name[0], sizeof(equipment)); strlcat(equipment, u_race(u)->_name, sizeof(equipment));
strlcat(equipment, "_unit", sizeof(equipment)); strlcat(equipment, "_unit", sizeof(equipment));
equip_unit(unew, get_equipment(equipment)); equip_unit(unew, get_equipment(equipment));

View File

@ -228,7 +228,7 @@ void give_men(int n, unit * u, unit * u2, struct order *ord)
error = 312; error = 312;
} }
else if (u2 && u2->number != 0 && u_race(u2) != u_race(u)) { else if (u2 && u2->number != 0 && u_race(u2) != u_race(u)) {
log_debug("faction %s attempts to give %s to %s.\n", itoa36(u->faction->no), u_race(u)->_name[0], u_race(u2)->_name[0]); log_debug("faction %s attempts to give %s to %s.\n", itoa36(u->faction->no), u_race(u)->_name, u_race(u2)->_name);
error = 139; error = 139;
} }
else if (u2 != NULL && (get_racename(u2->attribs) else if (u2 != NULL && (get_racename(u2->attribs)

View File

@ -216,7 +216,7 @@ static int lua_initfamiliar(unit * u)
int result = -1; int result = -1;
strlcpy(fname, "initfamiliar_", sizeof(fname)); strlcpy(fname, "initfamiliar_", sizeof(fname));
strlcat(fname, u_race(u)->_name[0], sizeof(fname)); strlcat(fname, u_race(u)->_name, sizeof(fname));
lua_getglobal(L, fname); lua_getglobal(L, fname);
if (lua_isfunction(L, -1)) { if (lua_isfunction(L, -1)) {
@ -237,7 +237,7 @@ static int lua_initfamiliar(unit * u)
create_mage(u, M_GRAY); create_mage(u, M_GRAY);
strlcpy(fname, u_race(u)->_name[0], sizeof(fname)); strlcpy(fname, u_race(u)->_name, sizeof(fname));
strlcat(fname, "_familiar", sizeof(fname)); strlcat(fname, "_familiar", sizeof(fname));
equip_unit(u, get_equipment(fname)); equip_unit(u, get_equipment(fname));
return result; return result;
@ -345,7 +345,7 @@ lua_wage(const region * r, const faction * f, const race * rc, int in_turn)
if (lua_isfunction(L, -1)) { if (lua_isfunction(L, -1)) {
tolua_pushusertype(L, (void *)r, TOLUA_CAST "region"); tolua_pushusertype(L, (void *)r, TOLUA_CAST "region");
tolua_pushusertype(L, (void *)f, TOLUA_CAST "faction"); tolua_pushusertype(L, (void *)f, TOLUA_CAST "faction");
tolua_pushstring(L, rc ? rc->_name[0] : 0); tolua_pushstring(L, rc ? rc->_name : 0);
tolua_pushnumber(L, (lua_Number) in_turn); tolua_pushnumber(L, (lua_Number) in_turn);
if (lua_pcall(L, 3, 1, 0) != 0) { if (lua_pcall(L, 3, 1, 0) != 0) {

View File

@ -17,6 +17,7 @@ item.test.c
move.test.c move.test.c
order.test.c order.test.c
pool.test.c pool.test.c
race.test.c
reports.test.c reports.test.c
spellbook.test.c spellbook.test.c
curse.test.c curse.test.c

View File

@ -954,7 +954,7 @@ void kill_troop(troop dt)
i_merge(&du->items, &drops); i_merge(&du->items, &drops);
} }
} }
sprintf(eqname, "%s_spoils", u_race(du)->_name[0]); sprintf(eqname, "%s_spoils", u_race(du)->_name);
eq = get_equipment(eqname); eq = get_equipment(eqname);
if (eq != NULL) { if (eq != NULL) {
equip_items(&du->items, eq); equip_items(&du->items, eq);
@ -2066,7 +2066,7 @@ static void make_heroes(battle * b)
if (fval(u, UFL_HERO)) { if (fval(u, UFL_HERO)) {
int i; int i;
if (!playerrace(u_race(u))) { if (!playerrace(u_race(u))) {
log_error("Hero %s is a %s.\n", unitname(u), u_race(u)->_name[0]); log_error("Hero %s is a %s.\n", unitname(u), u_race(u)->_name);
} }
for (i = 0; i != u->number; ++i) { for (i = 0; i != u->number; ++i) {
fig->person[i].speed += (hero_speed - 1); fig->person[i].speed += (hero_speed - 1);

View File

@ -318,7 +318,7 @@ const char *dbrace(const struct race *rc)
char *zPtr = zText; char *zPtr = zText;
/* the english names are all in ASCII, so we don't need to worry about UTF8 */ /* the english names are all in ASCII, so we don't need to worry about UTF8 */
strcpy(zText, (const char *)LOC(get_locale("en"), rc_name(rc, 0))); strcpy(zText, (const char *)LOC(get_locale("en"), rc_name(rc, NAME_SINGULAR)));
while (*zPtr) { while (*zPtr) {
*zPtr = (char)(toupper(*zPtr)); *zPtr = (char)(toupper(*zPtr));
++zPtr; ++zPtr;
@ -1838,9 +1838,9 @@ void init_locale(const struct locale *lang)
for (rc = races; rc; rc = rc->next) { for (rc = races; rc; rc = rc->next) {
const char *name; const char *name;
var.v = (void *)rc; var.v = (void *)rc;
name = LOC(lang, rc_name(rc, 1)); name = LOC(lang, rc_name(rc, NAME_PLURAL));
if (name) addtoken(tokens, name, var); if (name) addtoken(tokens, name, var);
name = LOC(lang, rc_name(rc, 0)); name = LOC(lang, rc_name(rc, NAME_SINGULAR));
if (name) addtoken(tokens, name, var); if (name) addtoken(tokens, name, var);
} }

View File

@ -255,7 +255,7 @@ unit *addplayer(region * r, faction * f)
u = createunit(r, f, 1, f->race); u = createunit(r, f, 1, f->race);
equip_items(&u->faction->items, get_equipment("new_faction")); equip_items(&u->faction->items, get_equipment("new_faction"));
equip_unit(u, get_equipment("first_unit")); equip_unit(u, get_equipment("first_unit"));
sprintf(buffer, "first_%s", u_race(u)->_name[0]); sprintf(buffer, "first_%s", u_race(u)->_name);
equip_unit(u, get_equipment(buffer)); equip_unit(u, get_equipment(buffer));
u->hp = unit_max_hp(u) * u->number; u->hp = unit_max_hp(u) * u->number;
fset(u, UFL_ISNEW); fset(u, UFL_ISNEW);

View File

@ -1157,7 +1157,7 @@ static item *default_spoil(const struct race *rc, int size)
char spoilname[32]; char spoilname[32];
const item_type *itype; const item_type *itype;
sprintf(spoilname, "%sspoil", rc->_name[0]); sprintf(spoilname, "%sspoil", rc->_name);
itype = it_find(spoilname); itype = it_find(spoilname);
if (itype != NULL) { if (itype != NULL) {
i_add(&itm, i_new(itype, size)); i_add(&itm, i_new(itype, size));

View File

@ -112,7 +112,7 @@ static void test_findrace(CuTest *tc) {
json_config(json); json_config(json);
rc = findrace("Zwerg", lang); rc = findrace("Zwerg", lang);
CuAssertPtrNotNull(tc, rc); CuAssertPtrNotNull(tc, rc);
CuAssertStrEquals(tc, "dwarf", rc->_name[0]); CuAssertStrEquals(tc, "dwarf", rc->_name);
} }
static void test_items(CuTest * tc) static void test_items(CuTest * tc)

View File

@ -45,7 +45,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
const char *describe_braineater(unit * u, const struct locale *lang) static const char *describe_braineater(unit * u, const struct locale *lang)
{ {
return LOC(lang, "describe_braineater"); return LOC(lang, "describe_braineater");
} }
@ -94,7 +94,8 @@ static const char *make_names(const char *monster, int *num_postfix,
/* nur 50% aller Namen haben "Nach-Teil", wenn kein Vor-Teil */ /* nur 50% aller Namen haben "Nach-Teil", wenn kein Vor-Teil */
if (uv >= *num_prefix) { if (uv >= *num_prefix) {
un = rng_int() % *num_postfix; un = rng_int() % *num_postfix;
} else { }
else {
un = rng_int() % (*num_postfix * ppostfix); un = rng_int() % (*num_postfix * ppostfix);
} }
@ -124,25 +125,25 @@ static const char *make_names(const char *monster, int *num_postfix,
return name; return name;
} }
const char *undead_name(const unit * u) static const char *undead_name(const unit * u)
{ {
static int num_postfix, num_name, num_prefix; static int num_postfix, num_name, num_prefix;
return make_names("undead", &num_postfix, 2, &num_name, &num_prefix, 2); return make_names("undead", &num_postfix, 2, &num_name, &num_prefix, 2);
} }
const char *skeleton_name(const unit * u) static const char *skeleton_name(const unit * u)
{ {
static int num_postfix, num_name, num_prefix; static int num_postfix, num_name, num_prefix;
return make_names("skeleton", &num_postfix, 5, &num_name, &num_prefix, 2); return make_names("skeleton", &num_postfix, 5, &num_name, &num_prefix, 2);
} }
const char *zombie_name(const unit * u) static const char *zombie_name(const unit * u)
{ {
static int num_postfix, num_name, num_prefix; static int num_postfix, num_name, num_prefix;
return make_names("zombie", &num_postfix, 5, &num_name, &num_prefix, 2); return make_names("zombie", &num_postfix, 5, &num_name, &num_prefix, 2);
} }
const char *ghoul_name(const unit * u) static const char *ghoul_name(const unit * u)
{ {
static int num_postfix, num_name, num_prefix; static int num_postfix, num_name, num_prefix;
return make_names("ghoul", &num_postfix, 5, &num_name, &num_prefix, 4); return make_names("ghoul", &num_postfix, 5, &num_name, &num_prefix, 4);
@ -213,15 +214,13 @@ const char *silbe3[SIL3] = {
"bus", "bus",
}; };
const char *generic_name(const unit * u) static const char *generic_name(const unit * u)
{ {
if (u->no == 1) { const char * name = rc_name(u_race(u), (u->no == 1) ? NAME_SINGULAR : NAME_PLURAL);
return LOC(u->faction->locale, mkname("race", u_race(u)->_name[0])); return LOC(u->faction->locale, name);
}
return LOC(u->faction->locale, mkname("race", u_race(u)->_name[1]));
} }
const char *dragon_name(const unit * u) static const char *dragon_name(const unit * u)
{ {
static char name[NAMESIZE + 1]; static char name[NAMESIZE + 1];
int rnd, ter = 0; int rnd, ter = 0;
@ -280,7 +279,8 @@ const char *dragon_name(const unit * u)
/* TODO: GERMAN */ /* TODO: GERMAN */
sprintf(name, "Die %sn von %s", no_article + 1, rname(u->region, sprintf(name, "Die %sn von %s", no_article + 1, rname(u->region,
default_locale)); default_locale));
} else { }
else {
char n[32]; char n[32];
strcpy(n, silbe1[rng_int() % SIL1]); strcpy(n, silbe1[rng_int() % SIL1]);
@ -288,7 +288,8 @@ const char *dragon_name(const unit * u)
strcat(n, silbe3[rng_int() % SIL3]); strcat(n, silbe3[rng_int() % SIL3]);
if (rng_int() % 5 > 2) { if (rng_int() % 5 > 2) {
sprintf(name, "%s, %s", n, str); /* "Name, der Titel" */ sprintf(name, "%s, %s", n, str); /* "Name, der Titel" */
} else { }
else {
strcpy(name, (const char *)str); /* "Der Titel Name" */ strcpy(name, (const char *)str); /* "Der Titel Name" */
name[0] = (char)toupper(name[0]); /* TODO: UNICODE - should use towupper() */ name[0] = (char)toupper(name[0]); /* TODO: UNICODE - should use towupper() */
strcat(name, " "); strcat(name, " ");
@ -352,7 +353,7 @@ static const char *drac_suf[DRAC_SUF] = {
"k" "k"
}; };
const char *dracoid_name(const unit * u) static const char *dracoid_name(const unit * u)
{ {
static char name[NAMESIZE + 1]; static char name[NAMESIZE + 1];
int mid_syllabels; int mid_syllabels;

View File

@ -22,13 +22,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" { extern "C" {
#endif #endif
extern void register_names(void); extern void register_names(void);
const char *undead_name(const struct unit *u);
const char *skeleton_name(const struct unit *u);
const char *zombie_name(const struct unit *u);
const char *ghoul_name(const struct unit *u);
const char *dragon_name(const struct unit *u);
const char *dracoid_name(const struct unit *u);
const char *generic_name(const struct unit *u);
const char *abkz(const char *s, char *buf, size_t size, size_t maxchars); const char *abkz(const char *s, char *buf, size_t size, size_t maxchars);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -82,6 +82,7 @@ static race * race_cache[MAXRACES];
struct race *get_race(race_t rt) { struct race *get_race(race_t rt) {
static int cache = -1; static int cache = -1;
const char * name; const char * name;
race * result = 0;
assert(rt < MAXRACES); assert(rt < MAXRACES);
name = racenames[rt]; name = racenames[rt];
@ -93,13 +94,12 @@ struct race *get_race(race_t rt) {
memset(race_cache, 0, sizeof(race_cache)); memset(race_cache, 0, sizeof(race_cache));
return race_cache[rt] = rc_get_or_create(name); return race_cache[rt] = rc_get_or_create(name);
} else { } else {
race * result = race_cache[rt]; result = race_cache[rt];
if (!result) { if (!result) {
result = race_cache[rt] = rc_get_or_create(name); result = race_cache[rt] = rc_get_or_create(name);
} }
return result;
} }
return 0; return result;
} }
race_list *get_familiarraces(void) race_list *get_familiarraces(void)
@ -151,7 +151,7 @@ static race *rc_find_i(const char *name)
const char *rname = name; const char *rname = name;
race *rc = races; race *rc = races;
while (rc && !strcmp(rname, rc->_name[0]) == 0) { while (rc && !strcmp(rname, rc->_name) == 0) {
rc = rc->next; rc = rc->next;
} }
return rc; return rc;
@ -177,13 +177,7 @@ race *rc_get_or_create(const char *zName)
assert(strchr(zName, ' ') == NULL); assert(strchr(zName, ' ') == NULL);
} }
strcpy(zBuffer, zName); strcpy(zBuffer, zName);
rc->_name[0] = _strdup(zBuffer); rc->_name = _strdup(zBuffer);
sprintf(zBuffer, "%s_p", zName);
rc->_name[1] = _strdup(zBuffer);
sprintf(zBuffer, "%s_d", zName);
rc->_name[2] = _strdup(zBuffer);
sprintf(zBuffer, "%s_x", zName);
rc->_name[3] = _strdup(zBuffer);
rc->precombatspell = NULL; rc->precombatspell = NULL;
rc->attack[0].type = AT_COMBATSPELL; rc->attack[0].type = AT_COMBATSPELL;
@ -220,19 +214,30 @@ extern void add_raceprefix(const char *prefix)
race_prefixes[next] = NULL; race_prefixes[next] = NULL;
} }
/* Die Bezeichnungen dürfen wegen der Art des Speicherns keine
* Leerzeichen enthalten! */
/* "den Zwergen", "Halblingsparteien" */
bool r_insectstalled(const region * r) bool r_insectstalled(const region * r)
{ {
return fval(r->terrain, ARCTIC_REGION); return fval(r->terrain, ARCTIC_REGION);
} }
const char *rc_name(const race * rc, int n) const char *rc_name(const race * rc, name_t n)
{ {
return rc ? mkname("race", rc->_name[n]) : NULL; const char * postfix = 0;
if (!rc) {
return NULL;
}
switch (n) {
case NAME_SINGULAR: postfix = ""; break;
case NAME_PLURAL: postfix = "_p"; break;
case NAME_DEFINITIVE: postfix = "_d"; break;
case NAME_CATEGORY: postfix = "_x"; break;
default: assert(!"invalid name_t enum in rc_name");
}
if (postfix) {
static char name[64]; // FIXME: static variable return
sprintf(name, "race::%s%s", rc->_name, postfix);
return name;
}
return NULL;
} }
const char *raceprefix(const unit * u) const char *raceprefix(const unit * u)
@ -271,8 +276,8 @@ const char *racename(const struct locale *loc, const unit * u, const race * rc)
return lbuf; return lbuf;
} }
str = LOC(loc, rc_name(rc, u->number != 1)); str = LOC(loc, rc_name(rc, (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL));
return str ? str : rc->_name[0]; return str ? str : rc->_name;
} }
int int
@ -313,7 +318,7 @@ rc_specialdamage(const race * ar, const race * dr,
void write_race_reference(const race * rc, struct storage *store) void write_race_reference(const race * rc, struct storage *store)
{ {
WRITE_TOK(store, rc ? rc->_name[0] : "none"); WRITE_TOK(store, rc ? rc->_name : "none");
} }
variant read_race_reference(struct storage *store) variant read_race_reference(struct storage *store)

View File

@ -119,7 +119,7 @@ extern "C" {
typedef struct race { typedef struct race {
struct param *parameters; struct param *parameters;
const char *_name[4]; /* neu: name[4]völker */ const char *_name; /* neu: name[4]völker */
float magres; float magres;
float maxaura; /* Faktor auf Maximale Aura */ float maxaura; /* Faktor auf Maximale Aura */
float regaura; /* Faktor auf Regeneration */ float regaura; /* Faktor auf Regeneration */
@ -134,7 +134,7 @@ extern "C" {
float aggression; /* chance that a monster will attack */ float aggression; /* chance that a monster will attack */
int hitpoints; int hitpoints;
const char *def_damage; const char *def_damage;
char armor; int armor;
int at_default; /* Angriffsskill Unbewaffnet (default: -2) */ int at_default; /* Angriffsskill Unbewaffnet (default: -2) */
int df_default; /* Verteidigungsskill Unbewaffnet (default: -2) */ int df_default; /* Verteidigungsskill Unbewaffnet (default: -2) */
int at_bonus; /* Verändert den Angriffsskill (default: 0) */ int at_bonus; /* Verändert den Angriffsskill (default: 0) */
@ -178,11 +178,13 @@ extern "C" {
extern race *rc_get_or_create(const char *name); extern race *rc_get_or_create(const char *name);
extern const race *rc_find(const char *); extern const race *rc_find(const char *);
extern const char *rc_name(const race *, int);
extern int rc_specialdamage(const race *, const race *, extern int rc_specialdamage(const race *, const race *,
const struct weapon_type *); const struct weapon_type *);
void free_races(void); void free_races(void);
typedef enum name_t { NAME_SINGULAR, NAME_PLURAL, NAME_DEFINITIVE, NAME_CATEGORY } name_t;
const char *rc_name(const race *, name_t);
/* Flags. Do not reorder these without changing json_race() in jsonconf.c */ /* Flags. Do not reorder these without changing json_race() in jsonconf.c */
#define RCF_NPC (1<<0) /* cannot be the race for a player faction (and other limits?) */ #define RCF_NPC (1<<0) /* cannot be the race for a player faction (and other limits?) */
#define RCF_KILLPEASANTS (1<<1) /* a monster that eats peasants */ #define RCF_KILLPEASANTS (1<<1) /* a monster that eats peasants */

24
src/kernel/race.test.c Normal file
View File

@ -0,0 +1,24 @@
#include <platform.h>
#include <kernel/config.h>
#include "race.h"
#include <CuTest.h>
#include <tests.h>
#include <stdlib.h>
#include <assert.h>
static void test_rc_name(CuTest *tc) {
struct race *rc = test_create_race("human");
CuAssertStrEquals(tc, "race::human", rc_name(rc, NAME_SINGULAR));
CuAssertStrEquals(tc, "race::human_p", rc_name(rc, NAME_PLURAL));
CuAssertStrEquals(tc, "race::human_d", rc_name(rc, NAME_DEFINITIVE));
CuAssertStrEquals(tc, "race::human_x", rc_name(rc, NAME_CATEGORY));
}
CuSuite *get_race_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_rc_name);
return suite;
}

View File

@ -386,12 +386,12 @@ static int dummy_data;
static region *dummy_ptr = (region *) & dummy_data; /* a funny hack */ static region *dummy_ptr = (region *) & dummy_data; /* a funny hack */
typedef struct uidhashentry { typedef struct uidhashentry {
unsigned int uid; int uid;
region *r; region *r;
} uidhashentry; } uidhashentry;
static uidhashentry uidhash[MAXREGIONS]; static uidhashentry uidhash[MAXREGIONS];
struct region *findregionbyid(unsigned int uid) struct region *findregionbyid(int uid)
{ {
int key = uid % MAXREGIONS; int key = uid % MAXREGIONS;
while (uidhash[key].uid != 0 && uidhash[key].uid != uid) while (uidhash[key].uid != 0 && uidhash[key].uid != uid)
@ -413,7 +413,7 @@ static void unhash_uid(region * r)
static void hash_uid(region * r) static void hash_uid(region * r)
{ {
unsigned int uid = r->uid; int uid = r->uid;
for (;;) { for (;;) {
if (uid != 0) { if (uid != 0) {
int key = uid % MAXREGIONS; int key = uid % MAXREGIONS;
@ -940,7 +940,7 @@ static region *last;
static unsigned int max_index = 0; static unsigned int max_index = 0;
region *new_region(int x, int y, struct plane *pl, unsigned int uid) region *new_region(int x, int y, struct plane *pl, int uid)
{ {
region *r; region *r;
@ -1409,7 +1409,7 @@ int resolve_region_id(variant id, void *address)
{ {
region *r = NULL; region *r = NULL;
if (id.i != 0) { if (id.i != 0) {
r = findregionbyid((unsigned int)id.i); r = findregionbyid(id.i);
if (r == NULL) { if (r == NULL) {
*(region **) address = NULL; *(region **) address = NULL;
return -1; return -1;

View File

@ -120,7 +120,7 @@ extern "C" {
/* an ascending number, to improve the speed of determining the interval in /* an ascending number, to improve the speed of determining the interval in
which a faction has its units. See the implementations of firstregion which a faction has its units. See the implementations of firstregion
and lastregion */ and lastregion */
unsigned int uid; /* a unique id */ int uid; /* a unique id */
int x, y; int x, y;
struct plane *_plane; /* to access, use rplane(r) */ struct plane *_plane; /* to access, use rplane(r) */
char *display; char *display;
@ -171,7 +171,7 @@ extern "C" {
int koor_distance(int ax, int ay, int bx, int by); int koor_distance(int ax, int ay, int bx, int by);
direction_t reldirection(const struct region *from, const struct region *to); direction_t reldirection(const struct region *from, const struct region *to);
struct region *findregion(int x, int y); struct region *findregion(int x, int y);
struct region *findregionbyid(unsigned int uid); struct region *findregionbyid(int uid);
extern struct attrib_type at_direction; extern struct attrib_type at_direction;
extern struct attrib_type at_moveblock; extern struct attrib_type at_moveblock;
@ -251,7 +251,7 @@ extern "C" {
const char *write_regionname(const struct region *r, const struct faction *f, const char *write_regionname(const struct region *r, const struct faction *f,
char *buffer, size_t size); char *buffer, size_t size);
struct region *new_region(int x, int y, struct plane *pl, unsigned int uid); struct region *new_region(int x, int y, struct plane *pl, int uid);
void remove_region(region ** rlist, region * r); void remove_region(region ** rlist, region * r);
void terraform_region(struct region *r, const struct terrain_type *terrain); void terraform_region(struct region *r, const struct terrain_type *terrain);
bool pnormalize(int *x, int *y, const struct plane *pl); bool pnormalize(int *x, int *y, const struct plane *pl);

View File

@ -331,14 +331,14 @@ void report_race(const struct unit *u, const char **name, const char **illusion)
if (illusion) { if (illusion) {
const race *irace = u_irace(u); const race *irace = u_irace(u);
if (irace && irace != u_race(u)) { if (irace && irace != u_race(u)) {
*illusion = irace->_name[0]; *illusion = irace->_name;
} }
else { else {
*illusion = NULL; *illusion = NULL;
} }
} }
if (name) { if (name) {
*name = u_race(u)->_name[0]; *name = u_race(u)->_name;
if (fval(u_race(u), RCF_SHAPESHIFTANY)) { if (fval(u_race(u), RCF_SHAPESHIFTANY)) {
const char *str = get_racename(u->attribs); const char *str = get_racename(u->attribs);
if (str) if (str)
@ -2166,7 +2166,7 @@ static void eval_race(struct opstack **stack, const void *userdata)
const struct locale *lang = report ? report->locale : default_locale; const struct locale *lang = report ? report->locale : default_locale;
int j = opop(stack).i; int j = opop(stack).i;
const race *r = (const race *)opop(stack).v; const race *r = (const race *)opop(stack).v;
const char *c = LOC(lang, rc_name(r, j != 1)); const char *c = LOC(lang, rc_name(r, (j == 1) ? NAME_SINGULAR : NAME_PLURAL));
size_t len = strlen(c); size_t len = strlen(c);
variant var; variant var;

View File

@ -810,8 +810,8 @@ void write_unit(struct gamedata *data, const unit * u)
WRITE_STR(data->store, u->display ? (const char *)u->display : ""); WRITE_STR(data->store, u->display ? (const char *)u->display : "");
WRITE_INT(data->store, u->number); WRITE_INT(data->store, u->number);
WRITE_INT(data->store, u->age); WRITE_INT(data->store, u->age);
WRITE_TOK(data->store, u_race(u)->_name[0]); WRITE_TOK(data->store, u_race(u)->_name);
WRITE_TOK(data->store, (irace && irace != u_race(u)) ? irace->_name[0] : ""); WRITE_TOK(data->store, (irace && irace != u_race(u)) ? irace->_name : "");
write_building_reference(u->building, data->store); write_building_reference(u->building, data->store);
write_ship_reference(u->ship, data->store); write_ship_reference(u->ship, data->store);
WRITE_INT(data->store, u->status); WRITE_INT(data->store, u->status);
@ -1412,7 +1412,7 @@ void writefaction(struct gamedata *data, const faction * f)
WRITE_TOK(data->store, locale_name(f->locale)); WRITE_TOK(data->store, locale_name(f->locale));
WRITE_INT(data->store, f->lastorders); WRITE_INT(data->store, f->lastorders);
WRITE_INT(data->store, f->age); WRITE_INT(data->store, f->age);
WRITE_TOK(data->store, f->race->_name[0]); WRITE_TOK(data->store, f->race->_name);
WRITE_SECTION(data->store); WRITE_SECTION(data->store);
WRITE_INT(data->store, f->magiegebiet); WRITE_INT(data->store, f->magiegebiet);

View File

@ -158,7 +158,7 @@ int rc_skillmod(const struct race *rc, const region * r, skill_t sk)
return 0; return 0;
} }
#ifdef FASTER_SKILLMOD #ifdef FASTER_SKILLMOD
unsigned int index = hashstring(rc->_name[0]) % RCMODMAXHASH; unsigned int index = hashstring(rc->_name) % RCMODMAXHASH;
struct skillmods **imods = &modhash[index]; struct skillmods **imods = &modhash[index];
while (*imods && (*imods)->race != rc) { while (*imods && (*imods)->race != rc) {
imods = &(*imods)->next; imods = &(*imods)->next;

View File

@ -1781,7 +1781,7 @@ static int parse_races(xmlDocPtr doc)
rc->study_speed[sk] = (char)speed; rc->study_speed[sk] = (char)speed;
} }
} else { } else {
log_error("unknown skill '%s' in race '%s'\n", (const char *)propValue, rc->_name[0]); log_error("unknown skill '%s' in race '%s'\n", (const char *)propValue, rc->_name);
} }
xmlFree(propValue); xmlFree(propValue);
} }
@ -1796,7 +1796,7 @@ static int parse_races(xmlDocPtr doc)
parse_function(node, &fun, &propValue); parse_function(node, &fun, &propValue);
if (fun == NULL) { if (fun == NULL) {
log_error("unknown function name '%s' for race %s\n", (const char *)propValue, rc->_name[0]); log_error("unknown function name '%s' for race %s\n", (const char *)propValue, rc->_name);
xmlFree(propValue); xmlFree(propValue);
continue; continue;
} }
@ -1816,7 +1816,7 @@ static int parse_races(xmlDocPtr doc)
} else if (strcmp((const char *)propValue, "initfamiliar") == 0) { } else if (strcmp((const char *)propValue, "initfamiliar") == 0) {
rc->init_familiar = (void (*)(struct unit *))fun; rc->init_familiar = (void (*)(struct unit *))fun;
} else { } else {
log_error("unknown function type '%s' for race %s\n", (const char *)propValue, rc->_name[0]); log_error("unknown function type '%s' for race %s\n", (const char *)propValue, rc->_name);
} }
xmlFree(propValue); xmlFree(propValue);
} }
@ -1871,7 +1871,7 @@ static int parse_races(xmlDocPtr doc)
if (attack->data.sp) { if (attack->data.sp) {
attack->level = xml_ivalue(node, "level", 0); attack->level = xml_ivalue(node, "level", 0);
if (attack->level <= 0) { if (attack->level <= 0) {
log_error("magical attack '%s' for race '%s' needs a level: %d\n", attack->data.sp->sname, rc->_name[0], attack->level); log_error("magical attack '%s' for race '%s' needs a level: %d\n", attack->data.sp->sname, rc->_name, attack->level);
} }
} }
} }

View File

@ -1006,7 +1006,7 @@ static void inactivefaction(faction * f)
if (inactiveFILE) { if (inactiveFILE) {
fprintf(inactiveFILE, "%s:%s:%d:%d\n", fprintf(inactiveFILE, "%s:%s:%d:%d\n",
factionid(f), factionid(f),
LOC(default_locale, rc_name(f->race, 1)), LOC(default_locale, rc_name(f->race, NAME_PLURAL)),
modify(count_all(f)), turn - f->lastorders); modify(count_all(f)), turn - f->lastorders);
fclose(inactiveFILE); fclose(inactiveFILE);
@ -1707,7 +1707,7 @@ int prefix_cmd(unit * u, struct order *ord)
init_order(ord); init_order(ord);
s = getstrtoken(); s = getstrtoken();
if (!*s) { if (!s || !*s) {
attrib *a = NULL; attrib *a = NULL;
if (fval(u, UFL_GROUP)) { if (fval(u, UFL_GROUP)) {
a = a_find(u->attribs, &at_group); a = a_find(u->attribs, &at_group);
@ -1721,8 +1721,7 @@ int prefix_cmd(unit * u, struct order *ord)
} }
return 0; return 0;
} }
else if (findtoken(in->names, s, &var) == E_TOK_NOMATCH) {
if (findtoken(in->names, s, &var) == E_TOK_NOMATCH) {
return 0; return 0;
} }
else if (race_prefixes[var.i] == NULL) { else if (race_prefixes[var.i] == NULL) {
@ -2524,13 +2523,13 @@ static bool display_race(faction * f, unit * u, const race * rc)
if (u && u_race(u) != rc) if (u && u_race(u) != rc)
return false; return false;
name = rc_name(rc, 0); name = rc_name(rc, NAME_SINGULAR);
bytes = slprintf(bufp, size, "%s: ", LOC(f->locale, name)); bytes = slprintf(bufp, size, "%s: ", LOC(f->locale, name));
if (wrptr(&bufp, &size, bytes) != 0) if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER(); WARN_STATIC_BUFFER();
key = mkname("raceinfo", rc->_name[0]); key = mkname("raceinfo", rc->_name);
info = locale_getstring(f->locale, key); info = locale_getstring(f->locale, key);
if (info == NULL) { if (info == NULL) {
info = locale_string(f->locale, mkname("raceinfo", "no_info")); info = locale_string(f->locale, mkname("raceinfo", "no_info"));

View File

@ -164,7 +164,7 @@ void score(void)
f->score, f->score - average_score_of_age(f->age, f->age / 24 + 1), f->score, f->score - average_score_of_age(f->age, f->age / 24 + 1),
((float)f->score / (float)allscores) * 100.0, ((float)f->score / (float)allscores) * 100.0,
(float)f->score / f->num_total, (float)f->score / f->num_total,
f->name, LOC(default_locale, rc_name(f->race, 0)), factionid(f), f->name, LOC(default_locale, rc_name(f->race, NAME_SINGULAR)), factionid(f),
f->age); f->age);
} }
fclose(scoreFP); fclose(scoreFP);

View File

@ -919,7 +919,7 @@ void spawn_dragons(void)
if (verbosity >= 2) { if (verbosity >= 2) {
log_printf(stdout, "%d %s in %s.\n", u->number, log_printf(stdout, "%d %s in %s.\n", u->number,
LOC(default_locale, LOC(default_locale,
rc_name(u_race(u), u->number != 1)), regionname(r, NULL)); rc_name(u_race(u), (u->number==1) ? NAME_SINGULAR:NAME_PLURAL)), regionname(r, NULL));
} }
name_unit(u); name_unit(u);
@ -994,7 +994,7 @@ void spawn_undead(void)
if (verbosity >= 2) { if (verbosity >= 2) {
log_printf(stdout, "%d %s in %s.\n", u->number, log_printf(stdout, "%d %s in %s.\n", u->number,
LOC(default_locale, LOC(default_locale,
rc_name(u_race(u), u->number != 1)), regionname(r, NULL)); rc_name(u_race(u), (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL)), regionname(r, NULL));
} }
{ {

View File

@ -43,7 +43,7 @@ static void oldfamiliars(unit * u)
char fname[64]; char fname[64];
/* these familiars have no special skills. /* these familiars have no special skills.
*/ */
_snprintf(fname, sizeof(fname), "%s_familiar", u_race(u)->_name[0]); _snprintf(fname, sizeof(fname), "%s_familiar", u_race(u)->_name);
create_mage(u, M_GRAY); create_mage(u, M_GRAY);
equip_unit(u, get_equipment(fname)); equip_unit(u, get_equipment(fname));
} }

View File

@ -2174,7 +2174,7 @@ const char *charset)
centre(F, gamedate_season(f->locale), true); centre(F, gamedate_season(f->locale), true);
rnl(F); rnl(F);
sprintf(buf, "%s, %s/%s (%s)", factionname(f), sprintf(buf, "%s, %s/%s (%s)", factionname(f),
LOC(f->locale, rc_name(f->race, 1)), LOC(f->locale, rc_name(f->race, NAME_PLURAL)),
LOC(f->locale, mkname("school", magic_school[f->magiegebiet])), f->email); LOC(f->locale, mkname("school", magic_school[f->magiegebiet])), f->email);
centre(F, buf, true); centre(F, buf, true);
if (f_get_alliance(f)) { if (f_get_alliance(f)) {

View File

@ -480,7 +480,7 @@ static const race *select_familiar(const race * magerace, magic_t magiegebiet)
retval = magerace->familiars[0]; retval = magerace->familiars[0];
} }
if (!retval) { if (!retval) {
log_error("select_familiar: No familiar (not even a default) defined for %s.\n", magerace->_name[0]); log_error("select_familiar: No familiar (not even a default) defined for %s.\n", magerace->_name);
} }
return retval; return retval;
} }
@ -494,7 +494,7 @@ static void make_familiar(unit * familiar, unit * mage)
if (u_race(familiar)->init_familiar != NULL) { if (u_race(familiar)->init_familiar != NULL) {
u_race(familiar)->init_familiar(familiar); u_race(familiar)->init_familiar(familiar);
} else { } else {
log_error("could not perform initialization for familiar %s.\n", familiar->faction->race->_name[0]); log_error("could not perform initialization for familiar %s.\n", familiar->faction->race->_name);
} }
/* triggers: */ /* triggers: */
@ -524,7 +524,7 @@ static int sp_summon_familiar(castorder * co)
} }
rc = select_familiar(mage->faction->race, mage->faction->magiegebiet); rc = select_familiar(mage->faction->race, mage->faction->magiegebiet);
if (rc == NULL) { if (rc == NULL) {
log_error("could not find suitable familiar for %s.\n", mage->faction->race->_name[0]); log_error("could not find suitable familiar for %s.\n", mage->faction->race->_name);
return 0; return 0;
} }
@ -1482,7 +1482,7 @@ static int sp_create_irongolem(castorder * co)
ADDMSG(&mage->faction->msgs, ADDMSG(&mage->faction->msgs,
msg_message("magiccreate_effect", "region command unit amount object", msg_message("magiccreate_effect", "region command unit amount object",
mage->region, co->order, mage, number, mage->region, co->order, mage, number,
LOC(mage->faction->locale, rc_name(rc_find("irongolem"), 1)))); LOC(mage->faction->locale, rc_name(rc_find("irongolem"), (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
return cast_level; return cast_level;
} }
@ -1543,7 +1543,7 @@ static int sp_create_stonegolem(castorder * co)
ADDMSG(&mage->faction->msgs, ADDMSG(&mage->faction->msgs,
msg_message("magiccreate_effect", "region command unit amount object", msg_message("magiccreate_effect", "region command unit amount object",
mage->region, co->order, mage, number, mage->region, co->order, mage, number,
LOC(mage->faction->locale, rc_name(rc_find("stonegolem"), 1)))); LOC(mage->faction->locale, rc_name(rc_find("stonegolem"), (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
return cast_level; return cast_level;
} }
@ -5551,7 +5551,7 @@ int sp_showastral(castorder * co)
} }
icat(u->number); icat(u->number);
scat(" "); scat(" ");
scat(LOC(mage->faction->locale, rc_name(u_race(u), u->number != 1))); scat(LOC(mage->faction->locale, rc_name(u_race(u), (u->number==1) ? NAME_SINGULAR:NAME_PLURAL)));
scat(", Entfernung "); scat(", Entfernung ");
icat(distance(rl2->data, rt)); icat(distance(rl2->data, rt));
scat(")"); scat(")");

View File

@ -111,7 +111,7 @@ static int insert_faction(sqlite3 *db, int game_id, faction *f) {
sqlite3_stmt *stmt = 0; sqlite3_stmt *stmt = 0;
sqlite3_prepare_v2(db, sql, -1, &stmt, 0); sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
sqlite3_bind_int(stmt, 1, game_id); sqlite3_bind_int(stmt, 1, game_id);
sqlite3_bind_text(stmt, 2, f->race->_name[0], -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, f->race->_name, -1, SQLITE_STATIC);
sqlite3_step(stmt); sqlite3_step(stmt);
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return (int)sqlite3_last_insert_rowid(db); return (int)sqlite3_last_insert_rowid(db);

View File

@ -92,11 +92,11 @@ static void out_faction(FILE * file, const struct faction *f)
if (alliances != NULL) { if (alliances != NULL) {
fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n", fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n",
f->name, itoa36(f->no), f_get_alliance(f) ? f->alliance->id : 0, f->name, itoa36(f->no), f_get_alliance(f) ? f->alliance->id : 0,
LOC(default_locale, rc_name(f->race, 0)), magic_school[f->magiegebiet], LOC(default_locale, rc_name(f->race, NAME_SINGULAR)), magic_school[f->magiegebiet],
count_units(f), f->num_total, f->money, turn - f->lastorders); count_units(f), f->num_total, f->money, turn - f->lastorders);
} else { } else {
fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n", fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n",
factionname(f), LOC(default_locale, rc_name(f->race, 0)), factionname(f), LOC(default_locale, rc_name(f->race, NAME_SINGULAR)),
magic_school[f->magiegebiet], count_units(f), f->num_total, f->money, magic_school[f->magiegebiet], count_units(f), f->num_total, f->money,
turn - f->lastorders); turn - f->lastorders);
} }
@ -185,7 +185,7 @@ void report_summary(summary * s, summary * o, bool full)
if (i != RC_TEMPLATE && i != RC_CLONE && s->factionrace[i]) { if (i != RC_TEMPLATE && i != RC_CLONE && s->factionrace[i]) {
const race *rc = get_race(i); const race *rc = get_race(i);
if (rc && playerrace(rc)) { if (rc && playerrace(rc)) {
fprintf(F, "%13s%s: %s\n", LOC(default_locale, rc_name(rc, 3)), fprintf(F, "%13s%s: %s\n", LOC(default_locale, rc_name(rc, NAME_CATEGORY)),
LOC(default_locale, "stat_tribe_p"), pcomp(s->factionrace[i], LOC(default_locale, "stat_tribe_p"), pcomp(s->factionrace[i],
o->factionrace[i])); o->factionrace[i]));
} }
@ -215,7 +215,7 @@ void report_summary(summary * s, summary * o, bool full)
for (i = 0; i < MAXRACES; i++) { for (i = 0; i < MAXRACES; i++) {
if (s->poprace[i]) { if (s->poprace[i]) {
const race *rc = get_race(i); const race *rc = get_race(i);
fprintf(F, "%20s: %s\n", LOC(default_locale, rc_name(rc, 1)), fprintf(F, "%20s: %s\n", LOC(default_locale, rc_name(rc, NAME_PLURAL)),
rcomp(s->poprace[i], o->poprace[i])); rcomp(s->poprace[i], o->poprace[i]));
} }
} }
@ -224,7 +224,7 @@ void report_summary(summary * s, summary * o, bool full)
if (i != RC_TEMPLATE && i != RC_CLONE && s->poprace[i]) { if (i != RC_TEMPLATE && i != RC_CLONE && s->poprace[i]) {
const race *rc = get_race(i); const race *rc = get_race(i);
if (playerrace(rc)) { if (playerrace(rc)) {
fprintf(F, "%20s: %s\n", LOC(default_locale, rc_name(rc, 1)), fprintf(F, "%20s: %s\n", LOC(default_locale, rc_name(rc, NAME_PLURAL)),
rcomp(s->poprace[i], o->poprace[i])); rcomp(s->poprace[i], o->poprace[i]));
} }
} }

View File

@ -27,6 +27,7 @@ int RunAllTests(void)
ADD_TESTS(suite, skill); ADD_TESTS(suite, skill);
ADD_TESTS(suite, keyword); ADD_TESTS(suite, keyword);
ADD_TESTS(suite, order); ADD_TESTS(suite, order);
ADD_TESTS(suite, race);
/* util */ /* util */
ADD_TESTS(suite, config); ADD_TESTS(suite, config);
ADD_TESTS(suite, base36); ADD_TESTS(suite, base36);