fix a bug in the escape_string function.

remove static return variable from escape_string.
add test coverage.
This commit is contained in:
Enno Rehling 2014-08-24 22:54:40 +02:00
parent 920902f8eb
commit a3037950ff
15 changed files with 340 additions and 312 deletions

View file

@ -490,11 +490,12 @@ static void report_crtypes(FILE * F, const struct locale *lang)
for (kmt = mtypehash[i]; kmt; kmt = kmt->nexthash) { for (kmt = mtypehash[i]; kmt; kmt = kmt->nexthash) {
const struct nrmessage_type *nrt = nrt_find(lang, kmt->mtype); const struct nrmessage_type *nrt = nrt_find(lang, kmt->mtype);
if (nrt) { if (nrt) {
char buffer[DISPLAYSIZE];
unsigned int hash = kmt->mtype->key; unsigned int hash = kmt->mtype->key;
assert(hash > 0); assert(hash > 0);
fprintf(F, "MESSAGETYPE %u\n", hash); fprintf(F, "MESSAGETYPE %u\n", hash);
fputc('\"', F); fputc('\"', F);
fputs(escape_string(nrt_string(nrt), NULL, 0), F); fputs(escape_string(nrt_string(nrt), buffer, sizeof(buffer)), F);
fputs("\";text\n", F); fputs("\";text\n", F);
fprintf(F, "\"%s\";section\n", nrt_section(nrt)); fprintf(F, "\"%s\";section\n", nrt_section(nrt));
} }

View file

@ -314,7 +314,7 @@ helpmode helpmodes[] = {
*/ */
const char *dbrace(const struct race *rc) const char *dbrace(const struct race *rc)
{ {
static char zText[32]; static char zText[32]; // FIXME: static return value
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 */
@ -974,7 +974,7 @@ int modifier)
#ifndef NDEBUG #ifndef NDEBUG
const char *strcheck(const char *s, size_t maxlen) const char *strcheck(const char *s, size_t maxlen)
{ {
static char buffer[16 * 1024]; static char buffer[16 * 1024]; // FIXME: static return value
if (strlen(s) > maxlen) { if (strlen(s) > maxlen) {
assert(maxlen < 16 * 1024); assert(maxlen < 16 * 1024);
log_warning("[strcheck] String wurde auf %d Zeichen verkürzt:\n%s\n", (int)maxlen, s); log_warning("[strcheck] String wurde auf %d Zeichen verkürzt:\n%s\n", (int)maxlen, s);
@ -1911,7 +1911,7 @@ int check_param(const struct param *p, const char *key, const char *searchvalue)
static const char *g_datadir; static const char *g_datadir;
const char *datapath(void) const char *datapath(void)
{ {
static char zText[MAX_PATH]; static char zText[MAX_PATH]; // FIXME: static return value
if (g_datadir) if (g_datadir)
return g_datadir; return g_datadir;
return strcat(strcpy(zText, basepath()), "/data"); return strcat(strcpy(zText, basepath()), "/data");
@ -1925,7 +1925,7 @@ void set_datapath(const char *path)
static const char *g_reportdir; static const char *g_reportdir;
const char *reportpath(void) const char *reportpath(void)
{ {
static char zText[MAX_PATH]; static char zText[MAX_PATH]; // FIXME: static return value
if (g_reportdir) if (g_reportdir)
return g_reportdir; return g_reportdir;
return strcat(strcpy(zText, basepath()), "/reports"); return strcat(strcpy(zText, basepath()), "/reports");

View file

@ -54,7 +54,7 @@ static const char *make_names(const char *monster, int *num_postfix,
int pprefix, int *num_name, int *num_prefix, int ppostfix) int pprefix, int *num_name, int *num_prefix, int ppostfix)
{ {
int uv, uu, un; int uv, uu, un;
static char name[NAMESIZE + 1]; static char name[NAMESIZE + 1]; // FIXME: static return value
char zText[32]; char zText[32];
const char *str; const char *str;
@ -222,7 +222,7 @@ static const char *generic_name(const unit * u)
static 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]; // FIXME: static return value
int rnd, ter = 0; int rnd, ter = 0;
int anzahl = 1; int anzahl = 1;
static int num_postfix; static int num_postfix;
@ -355,7 +355,7 @@ static const char *drac_suf[DRAC_SUF] = {
static 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]; // FIXME: static return value
int mid_syllabels; int mid_syllabels;
u = u; u = u;

View file

@ -1,7 +1,7 @@
/* /*
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de> Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de> Christian Schlittchen <corwin@amber.kn-bremen.de>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -93,7 +93,8 @@ struct race *get_race(race_t rt) {
cache = cache_breaker; cache = cache_breaker;
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 {
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);
@ -104,57 +105,57 @@ struct race *get_race(race_t rt) {
race_list *get_familiarraces(void) race_list *get_familiarraces(void)
{ {
static int init = 0; static int init = 0;
static race_list *familiarraces; static race_list *familiarraces;
if (!init) { if (!init) {
race *rc = races; race *rc = races;
for (; rc != NULL; rc = rc->next) { for (; rc != NULL; rc = rc->next) {
if (rc->init_familiar != NULL) { if (rc->init_familiar != NULL) {
racelist_insert(&familiarraces, rc); racelist_insert(&familiarraces, rc);
} }
}
init = false;
} }
init = false; return familiarraces;
}
return familiarraces;
} }
void racelist_clear(struct race_list **rl) void racelist_clear(struct race_list **rl)
{ {
while (*rl) { while (*rl) {
race_list *rl2 = (*rl)->next; race_list *rl2 = (*rl)->next;
free(*rl); free(*rl);
*rl = rl2; *rl = rl2;
} }
} }
void racelist_insert(struct race_list **rl, const struct race *r) void racelist_insert(struct race_list **rl, const struct race *r)
{ {
race_list *rl2 = (race_list *) malloc(sizeof(race_list)); race_list *rl2 = (race_list *)malloc(sizeof(race_list));
rl2->data = r; rl2->data = r;
rl2->next = *rl; rl2->next = *rl;
*rl = rl2; *rl = rl2;
} }
void free_races(void) { void free_races(void) {
while (races) { while (races) {
race * rc = races->next; race * rc = races->next;
free(races); free(races);
races =rc; races = rc;
} }
} }
static race *rc_find_i(const char *name) 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) { while (rc && !strcmp(rname, rc->_name) == 0) {
rc = rc->next; rc = rc->next;
} }
return rc; return rc;
} }
const race * rc_find(const char *name) { const race * rc_find(const char *name) {
@ -193,30 +194,30 @@ race *rc_get_or_create(const char *zName)
/** dragon movement **/ /** dragon movement **/
bool allowed_dragon(const region * src, const region * target) bool allowed_dragon(const region * src, const region * target)
{ {
if (fval(src->terrain, ARCTIC_REGION) && fval(target->terrain, SEA_REGION)) if (fval(src->terrain, ARCTIC_REGION) && fval(target->terrain, SEA_REGION))
return false; return false;
return allowed_fly(src, target); return allowed_fly(src, target);
} }
char **race_prefixes = NULL; char **race_prefixes = NULL;
extern void add_raceprefix(const char *prefix) extern void add_raceprefix(const char *prefix)
{ {
static size_t size = 4; static size_t size = 4;
static unsigned int next = 0; static unsigned int next = 0;
if (race_prefixes == NULL) if (race_prefixes == NULL)
race_prefixes = malloc(size * sizeof(char *)); race_prefixes = malloc(size * sizeof(char *));
if (next + 1 == size) { if (next + 1 == size) {
size *= 2; size *= 2;
race_prefixes = realloc(race_prefixes, size * sizeof(char *)); race_prefixes = realloc(race_prefixes, size * sizeof(char *));
} }
race_prefixes[next++] = _strdup(prefix); race_prefixes[next++] = _strdup(prefix);
race_prefixes[next] = NULL; race_prefixes[next] = NULL;
} }
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, name_t n) const char *rc_name(const race * rc, name_t n)
@ -233,7 +234,7 @@ const char *rc_name(const race * rc, name_t n)
default: assert(!"invalid name_t enum in rc_name"); default: assert(!"invalid name_t enum in rc_name");
} }
if (postfix) { if (postfix) {
static char name[64]; // FIXME: static variable return static char name[64]; // FIXME: static return value
sprintf(name, "race::%s%s", rc->_name, postfix); sprintf(name, "race::%s%s", rc->_name, postfix);
return name; return name;
} }
@ -242,97 +243,98 @@ const char *rc_name(const race * rc, name_t n)
const char *raceprefix(const unit * u) const char *raceprefix(const unit * u)
{ {
const attrib *asource = u->faction->attribs; const attrib *asource = u->faction->attribs;
if (fval(u, UFL_GROUP)) { if (fval(u, UFL_GROUP)) {
const attrib *agroup = agroup = a_findc(u->attribs, &at_group); const attrib *agroup = agroup = a_findc(u->attribs, &at_group);
if (agroup != NULL) if (agroup != NULL)
asource = ((const group *)(agroup->data.v))->attribs; asource = ((const group *)(agroup->data.v))->attribs;
} }
return get_prefix(asource); return get_prefix(asource);
} }
const char *racename(const struct locale *loc, const unit * u, const race * rc) const char *racename(const struct locale *loc, const unit * u, const race * rc)
{ {
const char *str, *prefix = raceprefix(u); const char *str, *prefix = raceprefix(u);
if (prefix != NULL) { if (prefix != NULL) {
static char lbuf[80]; static char lbuf[80]; // FIXME: static return value
char *bufp = lbuf; char *bufp = lbuf;
size_t size = sizeof(lbuf) - 1; size_t size = sizeof(lbuf) - 1;
int ch, bytes; int ch, bytes;
bytes = (int)strlcpy(bufp, LOC(loc, mkname("prefix", prefix)), size); bytes = (int)strlcpy(bufp, LOC(loc, mkname("prefix", prefix)), size);
if (wrptr(&bufp, &size, bytes) != 0) if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER(); WARN_STATIC_BUFFER();
bytes = (int)strlcpy(bufp, LOC(loc, rc_name(rc, u->number != 1)), size); bytes = (int)strlcpy(bufp, LOC(loc, rc_name(rc, u->number != 1)), size);
assert(~bufp[0] & 0x80 || !"unicode/not implemented"); assert(~bufp[0] & 0x80 || !"unicode/not implemented");
ch = tolower(*(unsigned char *)bufp); ch = tolower(*(unsigned char *)bufp);
bufp[0] = (char)ch; bufp[0] = (char)ch;
if (wrptr(&bufp, &size, bytes) != 0) if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER(); WARN_STATIC_BUFFER();
*bufp = 0; *bufp = 0;
return lbuf; return lbuf;
} }
str = LOC(loc, rc_name(rc, (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL)); str = LOC(loc, rc_name(rc, (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL));
return str ? str : rc->_name; return str ? str : rc->_name;
} }
int int
rc_specialdamage(const race * ar, const race * dr, rc_specialdamage(const race * ar, const race * dr,
const struct weapon_type *wtype) const struct weapon_type *wtype)
{ {
race_t art = old_race(ar); race_t art = old_race(ar);
int m, modifier = 0; int m, modifier = 0;
if (wtype != NULL && wtype->modifiers != NULL) if (wtype != NULL && wtype->modifiers != NULL)
for (m = 0; wtype->modifiers[m].value; ++m) { for (m = 0; wtype->modifiers[m].value; ++m) {
/* weapon damage for this weapon, possibly by race */ /* weapon damage for this weapon, possibly by race */
if (wtype->modifiers[m].flags & WMF_DAMAGE) { if (wtype->modifiers[m].flags & WMF_DAMAGE) {
race_list *rlist = wtype->modifiers[m].races; race_list *rlist = wtype->modifiers[m].races;
if (rlist != NULL) { if (rlist != NULL) {
while (rlist) { while (rlist) {
if (rlist->data == ar) if (rlist->data == ar)
break; break;
rlist = rlist->next; rlist = rlist->next;
} }
if (rlist == NULL) if (rlist == NULL)
continue; continue;
}
modifier += wtype->modifiers[m].value;
} }
modifier += wtype->modifiers[m].value; }
} switch (art) {
}
switch (art) {
case RC_HALFLING: case RC_HALFLING:
if (wtype != NULL && dragonrace(dr)) { if (wtype != NULL && dragonrace(dr)) {
modifier += 5; modifier += 5;
} }
break; break;
default: default:
break; break;
} }
return modifier; return modifier;
} }
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 : "none"); WRITE_TOK(store, rc ? rc->_name : "none");
} }
variant read_race_reference(struct storage *store) variant read_race_reference(struct storage *store)
{ {
variant result; variant result;
char zName[20]; char zName[20];
READ_TOK(store, zName, sizeof(zName)); READ_TOK(store, zName, sizeof(zName));
if (strcmp(zName, "none") == 0) { if (strcmp(zName, "none") == 0) {
result.v = NULL; result.v = NULL;
return result;
}
else {
result.v = rc_find_i(zName);
}
assert(result.v != NULL);
return result; return result;
} else {
result.v = rc_find_i(zName);
}
assert(result.v != NULL);
return result;
} }

View file

@ -107,7 +107,7 @@ const char *combatstatus[] = {
const char *report_kampfstatus(const unit * u, const struct locale *lang) const char *report_kampfstatus(const unit * u, const struct locale *lang)
{ {
static char fsbuf[64]; static char fsbuf[64]; // FIXME: static return value
strlcpy(fsbuf, LOC(lang, combatstatus[u->status]), sizeof(fsbuf)); strlcpy(fsbuf, LOC(lang, combatstatus[u->status]), sizeof(fsbuf));
if (fval(u, UFL_NOAID)) { if (fval(u, UFL_NOAID)) {
@ -1928,7 +1928,7 @@ f_regionid(const region * r, const faction * f, char *buffer, size_t size)
static char *f_regionid_s(const region * r, const faction * f) static char *f_regionid_s(const region * r, const faction * f)
{ {
static int i = 0; static int i = 0;
static char bufs[4][NAMESIZE + 20]; static char bufs[4][NAMESIZE + 20]; // FIXME: static return value
char *buf = bufs[(++i) % 4]; char *buf = bufs[(++i) % 4];
f_regionid(r, f, buf, NAMESIZE + 20); f_regionid(r, f, buf, NAMESIZE + 20);
@ -2177,7 +2177,7 @@ static void eval_race(struct opstack **stack, const void *userdata)
static void eval_order(struct opstack **stack, const void *userdata) static void eval_order(struct opstack **stack, const void *userdata)
{ /* order -> string */ { /* order -> string */
const struct order *ord = (const struct order *)opop(stack).v; const struct order *ord = (const struct order *)opop(stack).v;
static char buf[512]; char buf[512];
size_t len; size_t len;
variant var; variant var;
@ -2193,7 +2193,7 @@ static void eval_resources(struct opstack **stack, const void *userdata)
const faction *report = (const faction *)userdata; const faction *report = (const faction *)userdata;
const struct locale *lang = report ? report->locale : default_locale; const struct locale *lang = report ? report->locale : default_locale;
const struct resource *res = (const struct resource *)opop(stack).v; const struct resource *res = (const struct resource *)opop(stack).v;
static char buf[1024]; /* but we only use about half of this */ char buf[1024]; /* but we only use about half of this */
size_t size = sizeof(buf) - 1; size_t size = sizeof(buf) - 1;
variant var; variant var;
@ -2225,7 +2225,7 @@ static void eval_regions(struct opstack **stack, const void *userdata)
int i = opop(stack).i; int i = opop(stack).i;
int end, begin = opop(stack).i; int end, begin = opop(stack).i;
const arg_regions *regions = (const arg_regions *)opop(stack).v; const arg_regions *regions = (const arg_regions *)opop(stack).v;
static char buf[256]; char buf[256];
size_t size = sizeof(buf) - 1; size_t size = sizeof(buf) - 1;
variant var; variant var;
char *bufp = buf; char *bufp = buf;
@ -2262,7 +2262,7 @@ static void eval_trail(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 i, end = 0, begin = 0; int i, end = 0, begin = 0;
const arg_regions *regions = (const arg_regions *)opop(stack).v; const arg_regions *regions = (const arg_regions *)opop(stack).v;
static char buf[512]; char buf[512];
size_t size = sizeof(buf) - 1; size_t size = sizeof(buf) - 1;
variant var; variant var;
char *bufp = buf; char *bufp = buf;

View file

@ -241,7 +241,7 @@ static faction *factionorders(void)
} }
else { else {
log_warning("orders for invalid faction %s\n", itoa36(fid)); log_debug("orders for invalid faction %s\n", itoa36(fid));
} }
return f; return f;
} }

View file

@ -12,7 +12,7 @@
const char * keyword(keyword_t kwd) const char * keyword(keyword_t kwd)
{ {
static char result[KEYWORDSIZE]; static char result[KEYWORDSIZE]; // FIXME: static return value
if (!result[0]) { if (!result[0]) {
strcpy(result, "keyword::"); strcpy(result, "keyword::");
} }

View file

@ -100,7 +100,7 @@ extern int months_per_year;
static char *gamedate_season(const struct locale *lang) static char *gamedate_season(const struct locale *lang)
{ {
static char buf[256]; static char buf[256]; // FIXME: static return value
gamedate gd; gamedate gd;
get_gamedate(turn, &gd); get_gamedate(turn, &gd);
@ -517,7 +517,7 @@ void sparagraph(strlist ** SP, const char *s, int indent, char mark)
int i, j, width; int i, j, width;
int firstline; int firstline;
static char buf[REPORTWIDTH + 1]; static char buf[REPORTWIDTH + 1]; // FIXME: static return value
width = REPORTWIDTH - indent; width = REPORTWIDTH - indent;
firstline = 1; firstline = 1;

View file

@ -34,6 +34,7 @@ int RunAllTests(void)
ADD_TESTS(suite, bsdstring); ADD_TESTS(suite, bsdstring);
ADD_TESTS(suite, functions); ADD_TESTS(suite, functions);
ADD_TESTS(suite, umlaut); ADD_TESTS(suite, umlaut);
ADD_TESTS(suite, strings);
/* kernel */ /* kernel */
ADD_TESTS(suite, faction); ADD_TESTS(suite, faction);
ADD_TESTS(suite, build); ADD_TESTS(suite, build);

View file

@ -2,6 +2,7 @@ project(util C)
SET(_TEST_FILES SET(_TEST_FILES
base36.test.c base36.test.c
strings.test.c
bsdstring.test.c bsdstring.test.c
functions.test.c functions.test.c
umlaut.test.c umlaut.test.c

View file

@ -56,7 +56,7 @@ int atoi36(const char *str)
const char *itoab(int i, int base) const char *itoab(int i, int base)
{ {
static char **as = NULL; /* STATIC_RETURN: used for return, not across calls */ static char **as = NULL; // FIXME: static return value
char *s, *dst; char *s, *dst;
static int index = 0; /* STATIC_XCALL: used across calls */ static int index = 0; /* STATIC_XCALL: used across calls */
int neg = 0; int neg = 0;

View file

@ -1,7 +1,7 @@
/* /*
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de> Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de> Christian Schlittchen <corwin@amber.kn-bremen.de>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -36,17 +36,17 @@ locale *locales;
unsigned int locale_index(const locale * lang) unsigned int locale_index(const locale * lang)
{ {
assert(lang); assert(lang);
return lang->index; return lang->index;
} }
locale *get_locale(const char *name) locale *get_locale(const char *name)
{ {
unsigned int hkey = hashstring(name); unsigned int hkey = hashstring(name);
locale *l = locales; locale *l = locales;
while (l && l->hashkey != hkey) while (l && l->hashkey != hkey)
l = l->next; l = l->next;
return l; return l;
} }
static unsigned int nextlocaleindex = 0; static unsigned int nextlocaleindex = 0;
@ -59,7 +59,8 @@ locale *get_or_create_locale(const char *name)
if (!locales) { if (!locales) {
nextlocaleindex = 0; nextlocaleindex = 0;
} else { }
else {
while (*lp && (*lp)->hashkey != hkey) lp = &(*lp)->next; while (*lp && (*lp)->hashkey != hkey) lp = &(*lp)->next;
if (*lp) { if (*lp) {
return *lp; return *lp;
@ -81,172 +82,174 @@ locale *get_or_create_locale(const char *name)
*/ */
void make_locales(const char *str) void make_locales(const char *str)
{ {
const char *tok = str; const char *tok = str;
while (*tok) { while (*tok) {
char zText[32]; char zText[32];
while (*tok && *tok != ',') while (*tok && *tok != ',')
++tok; ++tok;
strncpy(zText, str, tok - str); strncpy(zText, str, tok - str);
zText[tok - str] = 0; zText[tok - str] = 0;
get_or_create_locale(zText); get_or_create_locale(zText);
if (*tok) { if (*tok) {
str = ++tok; str = ++tok;
}
} }
}
} }
const char *locale_getstring(const locale * lang, const char *key) const char *locale_getstring(const locale * lang, const char *key)
{ {
unsigned int hkey = hashstring(key); unsigned int hkey = hashstring(key);
unsigned int id = hkey & (SMAXHASH - 1); unsigned int id = hkey & (SMAXHASH - 1);
const struct locale_str *find; const struct locale_str *find;
assert(lang); assert(lang);
if (key == NULL || *key == 0) if (key == NULL || *key == 0)
return NULL; return NULL;
find = lang->strings[id]; find = lang->strings[id];
while (find) { while (find) {
if (find->hashkey == hkey) { if (find->hashkey == hkey) {
if (find->nexthash == NULL) { if (find->nexthash == NULL) {
/* if this is the only entry with this hash, fine. */ /* if this is the only entry with this hash, fine. */
assert(strcmp(key, find->key) == 0); assert(strcmp(key, find->key) == 0);
return find->str; return find->str;
} }
if (strcmp(key, find->key) == 0) { if (strcmp(key, find->key) == 0) {
return find->str; return find->str;
} }
}
find = find->nexthash;
} }
find = find->nexthash; return NULL;
}
return NULL;
} }
const char *locale_string(const locale * lang, const char *key) const char *locale_string(const locale * lang, const char *key)
{ {
assert(lang); assert(lang);
assert(key); assert(key);
if (key != NULL) { if (key != NULL) {
unsigned int hkey = hashstring(key); unsigned int hkey = hashstring(key);
unsigned int id = hkey & (SMAXHASH - 1); unsigned int id = hkey & (SMAXHASH - 1);
struct locale_str *find; struct locale_str *find;
if (*key == 0) return 0; if (*key == 0) return 0;
find = lang->strings[id]; find = lang->strings[id];
while (find) { while (find) {
if (find->hashkey == hkey) { if (find->hashkey == hkey) {
if (!find->nexthash) { if (!find->nexthash) {
/* if this is the only entry with this hash, fine. */ /* if this is the only entry with this hash, fine. */
assert(strcmp(key, find->key) == 0); assert(strcmp(key, find->key) == 0);
break; break;
}
if (strcmp(key, find->key) == 0)
break;
}
find = find->nexthash;
} }
if (strcmp(key, find->key) == 0) if (!find) {
break; log_warning("missing translation for \"%s\" in locale %s\n", key, lang->name);
} if (lang->fallback) {
find = find->nexthash; return locale_string(lang->fallback, key);
}
return 0;
}
return find->str;
} }
if (!find) { return 0;
log_warning("missing translation for \"%s\" in locale %s\n", key, lang->name);
if (lang->fallback) {
return locale_string(lang->fallback, key);
}
return 0;
}
return find->str;
}
return 0;
} }
void locale_setstring(locale * lang, const char *key, const char *value) void locale_setstring(locale * lang, const char *key, const char *value)
{ {
unsigned int hkey = hashstring(key); unsigned int hkey = hashstring(key);
unsigned int id = hkey & (SMAXHASH - 1); unsigned int id = hkey & (SMAXHASH - 1);
struct locale_str *find; struct locale_str *find;
if (!lang) { if (!lang) {
lang = default_locale; lang = default_locale;
} }
assert(lang); assert(lang);
find = lang->strings[id]; find = lang->strings[id];
while (find) { while (find) {
if (find->hashkey == hkey && strcmp(key, find->key) == 0) if (find->hashkey == hkey && strcmp(key, find->key) == 0)
break; break;
find = find->nexthash; find = find->nexthash;
} }
if (!find) { if (!find) {
find = calloc(1, sizeof(struct locale_str)); find = calloc(1, sizeof(struct locale_str));
find->nexthash = lang->strings[id]; find->nexthash = lang->strings[id];
lang->strings[id] = find; lang->strings[id] = find;
find->hashkey = hkey; find->hashkey = hkey;
find->key = _strdup(key); find->key = _strdup(key);
find->str = _strdup(value); find->str = _strdup(value);
} else { }
if (strcmp(find->str, value) != 0) { else {
log_warning("multiple translations for key %s\n", key); if (strcmp(find->str, value) != 0) {
log_warning("multiple translations for key %s\n", key);
}
free(find->str);
find->str = _strdup(value);
} }
free(find->str);
find->str = _strdup(value);
}
} }
const char *locale_name(const locale * lang) const char *locale_name(const locale * lang)
{ {
return lang ? lang->name : "(null)"; return lang ? lang->name : "(null)";
} }
char *mkname_buf(const char *space, const char *name, char *buffer) char *mkname_buf(const char *space, const char *name, char *buffer)
{ {
if (space && *space) { if (space && *space) {
sprintf(buffer, "%s::%s", space, name); sprintf(buffer, "%s::%s", space, name);
} else { }
strcpy(buffer, name); else {
} strcpy(buffer, name);
return buffer; }
return buffer;
} }
const char *mkname(const char *space, const char *name) const char *mkname(const char *space, const char *name)
{ {
static char zBuffer[128]; /* STATIC_RESULT: used for return, not across calls */ static char zBuffer[128]; // FIXME: static return value
return mkname_buf(space, name, zBuffer); return mkname_buf(space, name, zBuffer);
} }
locale *nextlocale(const struct locale * lang) locale *nextlocale(const struct locale * lang)
{ {
return lang->next; return lang->next;
} }
typedef struct lstr { typedef struct lstr {
void * tokens[UT_MAX]; void * tokens[UT_MAX];
} lstr; } lstr;
static lstr lstrs[MAXLOCALES]; static lstr lstrs[MAXLOCALES];
void ** get_translations(const struct locale *lang, int index) void ** get_translations(const struct locale *lang, int index)
{ {
assert(lang); assert(lang);
assert(lang->index < MAXLOCALES assert(lang->index < MAXLOCALES
|| "you have to increase MAXLOCALES and recompile"); || "you have to increase MAXLOCALES and recompile");
if (lang->index < MAXLOCALES) { if (lang->index < MAXLOCALES) {
return lstrs[lang->index].tokens + index; return lstrs[lang->index].tokens + index;
} }
return lstrs[0].tokens + index; return lstrs[0].tokens + index;
} }
void free_locales(void) void free_locales(void)
{ {
while (locales) { while (locales) {
int i; int i;
locale * next = locales->next; locale * next = locales->next;
for (i=0; i!=SMAXHASH; ++i) { for (i = 0; i != SMAXHASH; ++i) {
while (locales->strings[i]) { while (locales->strings[i]) {
struct locale_str * strings = locales->strings[i]; struct locale_str * strings = locales->strings[i];
free(strings->key); free(strings->key);
free(strings->str); free(strings->str);
locales->strings[i] = strings->nexthash; locales->strings[i] = strings->nexthash;
free(strings); free(strings);
} }
}
free(locales);
locales = next;
} }
free(locales);
locales = next;
}
} }

View file

@ -1,7 +1,7 @@
/* /*
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de> Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de> Christian Schlittchen <corwin@amber.kn-bremen.de>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -20,73 +20,72 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* libc includes */ /* libc includes */
#include <string.h> #include <string.h>
#include <assert.h>
unsigned int hashstring(const char *s) unsigned int hashstring(const char *s)
{ {
unsigned int key = 0; unsigned int key = 0;
while (*s) { while (*s) {
key = key * 37 + *s++; key = key * 37 + *s++;
} }
return key & 0x7FFFFFFF; return key & 0x7FFFFFFF;
} }
const char *escape_string(const char *str, char *buffer, const char *escape_string(const char *str, char *buffer,
unsigned int len) unsigned int len)
{ {
const char *start = strchr(str, '\"'); const char *start = strchr(str, '\"');
if (start) { if (!start) start = strchr(str, '\\');
static char s_buffer[4096]; /* STATIC_RESULT: used for return, not across calls */ assert(buffer);
const char *p; if (start) {
char *o; const char *p;
size_t skip = start - str; char *o;
size_t skip = start - str;
if (buffer == NULL) { memcpy(buffer, str, skip);
buffer = s_buffer; o = buffer + skip;
len = sizeof(s_buffer); p = str + skip;
do {
if (*p == '\"' || *p == '\\') {
if (len < 2) {
*o = '\0';
break;
}
(*o++) = '\\';
len -= 2;
}
else {
if (len < 1) {
*o = '\0';
break;
}
--len;
}
(*o++) = (*p);
} while (*p++);
return buffer;
} }
memcpy(buffer, str, skip); return str;
o = buffer + skip;
p = str + skip;
do {
if (*p == '\"' || *p == '\\') {
if (len < 2) {
*o = '\0';
break;
}
(*o++) = '\\';
len -= 2;
} else {
if (len < 1) {
*o = '\0';
break;
}
--len;
}
(*o++) = (*p);
} while (*p++);
return buffer;
}
return str;
} }
unsigned int jenkins_hash(unsigned int a) unsigned int jenkins_hash(unsigned int a)
{ {
a = (a + 0x7ed55d16) + (a << 12); a = (a + 0x7ed55d16) + (a << 12);
a = (a ^ 0xc761c23c) ^ (a >> 19); a = (a ^ 0xc761c23c) ^ (a >> 19);
a = (a + 0x165667b1) + (a << 5); a = (a + 0x165667b1) + (a << 5);
a = (a + 0xd3a2646c) ^ (a << 9); a = (a + 0xd3a2646c) ^ (a << 9);
a = (a + 0xfd7046c5) + (a << 3); a = (a + 0xfd7046c5) + (a << 3);
a = (a ^ 0xb55a4f09) ^ (a >> 16); a = (a ^ 0xb55a4f09) ^ (a >> 16);
return a; return a;
} }
unsigned int wang_hash(unsigned int a) unsigned int wang_hash(unsigned int a)
{ {
a = ~a + (a << 15); /* a = (a << 15) - a - 1; */ a = ~a + (a << 15); /* a = (a << 15) - a - 1; */
a = a ^ (a >> 12); a = a ^ (a >> 12);
a = a + (a << 2); a = a + (a << 2);
a = a ^ (a >> 4); a = a ^ (a >> 4);
a = a * 2057; /* a = (a + (a << 3)) + (a << 11); */ a = a * 2057; /* a = (a + (a << 3)) + (a << 11); */
a = a ^ (a >> 16); a = a ^ (a >> 16);
return a; return a;
} }

21
src/util/strings.test.c Normal file
View file

@ -0,0 +1,21 @@
#include <CuTest.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "goodies.h"
static void test_escape_string(CuTest * tc)
{
char scratch[64];
CuAssertStrEquals(tc, "hello world", escape_string("hello world", scratch, sizeof(scratch)));
CuAssertStrEquals(tc, "hello \\\"world\\\"", escape_string("hello \"world\"", scratch, sizeof(scratch)));
CuAssertStrEquals(tc, "\\\"\\\\", escape_string("\"\\", scratch, sizeof(scratch)));
CuAssertStrEquals(tc, "\\\\", escape_string("\\", scratch, sizeof(scratch)));
}
CuSuite *get_strings_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_escape_string);
return suite;
}

View file

@ -30,7 +30,7 @@
#ifdef USE_LIBXML2 #ifdef USE_LIBXML2
const xmlChar *xml_i(double number) const xmlChar *xml_i(double number)
{ {
static char buffer[128]; static char buffer[128]; // FIXME: static return value
_snprintf(buffer, sizeof(buffer), "%.0f", number); _snprintf(buffer, sizeof(buffer), "%.0f", number);
return (const xmlChar *)buffer; return (const xmlChar *)buffer;
} }