/*-
* Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: lang.c,v 1.10 1999/04/16 15:52:24 fjoe Exp $
*/
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include "merc.h"
#include "lang.h"
#include "db.h"
/*----------------------------------------------------------------------------
* main language support functions
*/
static const char*
word_form_lookup(lang_t *l, rulecl_t *rcl, const char *word, int fnum)
{
rule_t *rule;
char **p;
char *q;
static char buf[MAX_STRING_LENGTH];
if (!fnum || IS_NULLSTR(word))
return word;
/*
* variable part(s) of word can be specified by tildes
* (simple recursion)
*/
if ((q = strchr(word, '~'))) {
char buf2[MAX_STRING_LENGTH];
char buf3[MAX_STRING_LENGTH];
char *r;
/* copy prefix */
strnzncpy(buf2, sizeof(buf2), word, q-word);
/*
* translate infix, translation must be done
* before copying the result to buf[] because buf is
* static
*/
r = strchr(q+1, '~');
if (!r)
r = strchr(q+1, '\0');
strnzncpy(buf3, sizeof(buf3), q+1, *r ? r-q-1 : r-q);
strnzcat(buf2, sizeof(buf2),
word_form_lookup(l, rcl, buf3, fnum));
/* translate the rest */
if (!*r) {
strnzcpy(buf, sizeof(buf), buf2);
return buf;
}
if (strchr(r+1, '~')) {
strnzcpy(buf3, sizeof(buf3),
word_form_lookup(l, rcl, r+1, fnum));
q = buf3;
}
else
q = r+1;
strnzcpy(buf, sizeof(buf), buf2);
strnzcat(buf, sizeof(buf), q);
return buf;
}
/*
* explicit rule lookup
*/
if ((rule = erule_lookup(rcl, word)) == NULL) {
rule_t e;
/*
* implicit rule lookup
*/
if ((rule = irule_find(rcl, word)) == NULL)
return word;
/*
* implicit rule found - create explicit rule and use it
*/
erule_create(&e, rule, word);
rule = erule_add(rcl, &e);
SET_BIT(rcl->flags, RULES_EXPL_CHANGED);
}
if ((p = varr_get(&rule->f->v, fnum)) == NULL
|| IS_NULLSTR(*p))
return word;
if (rule->arg <= 0 || **p != '-')
return *p;
strnzcpy(buf, sizeof(buf), word);
strnzcpy(buf + rule->arg, sizeof(buf) - rule->arg, *p + 1);
return buf;
}
const char *word_form(const char *word, int fnum, int lang, int rulecl)
{
lang_t *l;
if ((rulecl < 0 || rulecl >= MAX_RULECL)
|| (l = varr_get(&langs, lang)) == NULL)
return word;
switch (rulecl) {
case RULES_GENDER:
fnum = (fnum + SEX_MAX - 1) % SEX_MAX;
break;
case RULES_QTY:
fnum %= 100;
if (fnum > 14)
fnum %= 10;
}
return word_form_lookup(l, l->rules + rulecl, word, fnum);
}
/*----------------------------------------------------------------------------
* vform_t functions
*/
vform_t *vform_new(void)
{
vform_t *f = calloc(1, sizeof(*f));
f->v.nsize = sizeof(char*);
f->v.nstep = 4;
f->ref = 1;
return f;
}
vform_t *vform_dup(vform_t *f)
{
f->ref++;
return f;
}
void vform_free(vform_t *f)
{
int i;
if (--f->ref)
return;
for (i = 0; i < f->v.nused; i++)
free_string(VARR_GET(&f->v, i));
varr_free(&f->v);
free(f);
}
void vform_add(vform_t *f, int fnum, const char *s)
{
const char **p = varr_touch(&f->v, fnum);
if (*p)
free_string(*p);
*p = str_dup(s);
}
void vform_del(vform_t *f, int fnum)
{
const char **p = varr_get(&f->v, fnum);
if (*p)
free_string(*p);
*p = NULL;
}
/*----------------------------------------------------------------------------
* rule_t functions
*/
#define rulehash(s) hashistr(s, 16, MAX_RULE_HASH)
/* reverse order (otherwise word_del will not work) */
static int cmprule(const void *p1, const void *p2)
{
return -str_cmp(((rule_t*) p1)->name, ((rule_t*) p2)->name);
}
void rule_init(rule_t *r)
{
r->name = NULL;
r->arg = 0;
r->f = vform_new();
}
void rule_clear(rule_t *r)
{
free_string(r->name);
vform_free(r->f);
r->name = NULL;
}
void erule_create(rule_t *expl, rule_t *impl, const char *word)
{
rule_init(expl);
expl->name = str_dup(word);
expl->arg = strlen(word) + impl->arg;
expl->f = vform_dup(impl->f);
}
/*----------------------------------------------------------------------------
* implicit rules operations
*/
rule_t *irule_add(rulecl_t *rcl, rule_t *r)
{
rule_t *rnew = varr_enew(&rcl->impl);
*rnew = *r;
return rnew;
}
rule_t *irule_insert(rulecl_t *rcl, size_t num, rule_t *r)
{
rule_t *rnew;
if (num > rcl->impl.nused)
num = rcl->impl.nused;
rnew = varr_insert(&rcl->impl, num);
*rnew = *r;
return rnew;
}
void irule_del(rulecl_t *rcl, rule_t *r)
{
rule_clear(r);
varr_del(&rcl->impl, r);
}
rule_t *irule_lookup(rulecl_t *rcl, const char *num)
{
char *q;
size_t i;
if (IS_NULLSTR(num))
return NULL;
i = strtoul(num, &q, 0);
if (*q || i >= rcl->impl.nused)
return NULL;
return VARR_GET(&rcl->impl, i);
}
rule_t *irule_find(rulecl_t *rcl, const char *word)
{
int i;
for (i = 0; i < rcl->impl.nused; i++) {
rule_t *r = VARR_GET(&rcl->impl, i);
if (r->name[0] == '-'
&& !strchr(word, ' ')
&& !str_suffix(r->name+1, word))
return r;
}
return NULL;
}
/*----------------------------------------------------------------------------
* explicit rules operations
*/
rule_t *erule_add(rulecl_t *rcl, rule_t *r)
{
rule_t *rnew;
varr *v;
if (IS_NULLSTR(r->name))
return NULL;
v = rcl->expl + rulehash(r->name);
if (varr_bsearch(v, r, cmprule))
return NULL;
rnew = varr_enew(v);
*rnew = *r;
varr_qsort(v, cmprule);
return varr_bsearch(v, r, cmprule);
}
void erule_del(rulecl_t *rcl, rule_t *r)
{
varr *v;
v = rcl->expl + rulehash(r->name);
rule_clear(r);
varr_qsort(v, cmprule);
v->nused--;
}
rule_t *erule_lookup(rulecl_t *rcl, const char *name)
{
if (IS_NULLSTR(name))
return NULL;
return varr_bsearch(rcl->expl + rulehash(name), &name, cmprule);
}
/*----------------------------------------------------------------------------
* rulecl_t functions
*/
static void rulecl_init(lang_t *l, int rulecl)
{
int i;
rulecl_t *rcl = l->rules + rulecl;
rcl->rulecl = rulecl;
for (i = 0; i < MAX_RULE_HASH; i++) {
rcl->expl[i].nsize = sizeof(rule_t);
rcl->expl[i].nstep = 4;
}
rcl->impl.nsize = sizeof(rule_t);
rcl->impl.nstep = 4;
}
/*----------------------------------------------------------------------------
* lang_t functions
*/
varr langs = { sizeof(lang_t), 2 };
lang_t *lang_new(void)
{
int i;
lang_t *l = varr_enew(&langs);
l->slang_of = -1;
l->vnum = langs.nused-1;
for (i = 0; i < MAX_RULECL; i++)
rulecl_init(l, i);
return l;
}
int lang_lookup(const char *name)
{
return lang_nlookup(name, strlen(name));
}
int lang_nlookup(const char *name, size_t len)
{
int lang;
if (IS_NULLSTR(name))
return -1;
for (lang = 0; lang < langs.nused; lang++) {
lang_t *l = VARR_GET(&langs, lang);
if (str_ncmp(l->name, name, len) == 0)
return lang;
}
if (fBootDb)
db_error("lang_lookup", "%s: unknown language", name);
return -1;
}