/*-
* 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: resource.c,v 1.43 1998/12/23 16:11:20 fjoe Exp $
*/
#if defined (SUNOS) || defined (WIN32)
# include "compat/compat.h"
# include <stdarg.h>
#endif
#include <limits.h>
#include <stdio.h>
#include <time.h>
#include "merc.h"
#ifdef SVR4
# include "compat/compat.h"
#endif
char BLANK_STRING[] = "{RBUG!!!{x";
struct msg {
const char** p;
int sexdep;
};
static int nmsgid;
struct msg** msg_table;
const char** lang_table;
int nlang;
enum {
DEP_NONE,
DEP_CHAR,
DEP_VICTIM
};
#define FIX_SEX(sex) ((sex) >= SEX_FEMALE ? SEX_FEMALE : \
(sex) <= SEX_NEUTRAL ? SEX_NEUTRAL : \
SEX_MALE)
const char *vmsg(int msgid, CHAR_DATA *ch, CHAR_DATA *victim)
{
struct msg *m;
if (msgid >= nmsgid || ch->lang >= nlang)
return BLANK_STRING;
m = msg_table[ch->lang]+msgid;
if (m->sexdep) {
if (m->sexdep == DEP_VICTIM)
ch = victim;
return m->p[ch ? FIX_SEX(ch->sex) : SEX_NEUTRAL];
}
else
return (char*)m->p;
}
/*
* msgdb implementation begins here
*/
#ifdef __FreeBSD__
# include <sysexits.h>
#else
# define EX_DATAERR 65
# define EX_NOINPUT 66
# define EX_OSERR 71
#endif
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "db/db.h"
struct msgid {
const char* name;
int msgid;
};
static struct msgid *msgid_table;
static int nalloc;
#define MSGID_ALLOC_STEP 16
static char LANG_LST[] = "lang.lst";
static char RESOURCE_H[] = "resource.h";
static char STR_START[] = "enum {\n";
static char STR_END[] = "};\n";
#define BUFSZ 1024
static void load_langfile(int lang, char* fname);
static void msgid_add(char* name, int msgid);
static msgid_lookup(char* name);
static msgid_cmp(const void*, const void*);
static const char* msgid_name_lookup(int msgid);
char *fix_msg(char* p)
{
static char buf[MAX_STRING_LENGTH];
char a[] = "\n\a";
char b[] = "nra";
int i;
for (i = 0; i < sizeof(buf)-2 && *p; i++, p++) {
char *q;
char c;
if ((q = strchr(a, *p))) {
buf[i++] = '\\';
c = b[q-a];
}
else
c = *p;
buf[i] = c;
}
buf[i] = '\0';
return buf;
}
void load_oldmsgdb(void)
{
int i;
FILE *f;
char buf[BUFSZ];
int line = 0;
int msgid = 0;
int search_start = 1;
/*
* load msg identifiers
*/
f = dfopen(MSGDB_PATH, RESOURCE_H, "r");
if (f == NULL) {
perror(RESOURCE_H);
exit(EX_NOINPUT);
}
while(fgets(buf, sizeof(buf), f) != NULL) {
static char SEP[] = " \t,";
char *p;
line++;
if (search_start) {
if (strcmp(buf, STR_START) == 0)
search_start = 0;
continue;
}
if (strcmp(buf, STR_END) == 0)
break;
/* strip trailing '\n' and check if line is too long */
p = strchr(buf, '\n');
if (p == NULL) {
fprintf(stderr, "%s:%d: line too long\n",
RESOURCE_H, line);
exit(EX_DATAERR);
}
else
*p = '\0';
/* skip separators */
for (p = buf; *p && strchr(SEP, *p) != NULL; p++);
if (*p == '\0')
continue;
msgid_add(strsep(&p, SEP), msgid++);
}
fclose(f);
if (msgid_table == NULL) {
fprintf(stderr, "%s: no msgids found\n", RESOURCE_H);
exit(EX_DATAERR);
}
qsort(msgid_table, nmsgid, sizeof(*msgid_table), msgid_cmp);
/*
* load language files
*/
f = dfopen(MSGDB_PATH, LANG_LST, "r");
if (f == NULL) {
perror(LANG_LST);
exit(EX_NOINPUT);
}
if (fscanf(f, "%d", &nlang) != 1 || nlang <= 0) {
fprintf(stderr, "%s: syntax error\n", LANG_LST);
exit(EX_DATAERR);
}
msg_table = alloc_perm(nlang * sizeof(*msg_table));
lang_table = alloc_perm((nlang+1) * sizeof(*lang_table));
lang_table[nlang] = NULL;
for (i = 0; i < nlang; i++) {
char buf2[BUFSZ];
if (fgets(buf, sizeof(buf), f) == NULL) {
fprintf(stderr, "%s: premature end of file\n",
LANG_LST);
exit(EX_DATAERR);
}
if (fscanf(f, "%s %s", buf, buf2) != 2) {
fprintf(stderr, "%s: syntax error\n", LANG_LST);
exit(EX_DATAERR);
}
lang_table[i] = str_dup(buf);
load_langfile(i, buf2);
}
fclose(f);
for (i = 0; i < nmsgid; i++)
free_string(msgid_table[i].name);
free(msgid_table);
}
static
void
load_langfile(int lang, char* fname)
{
int i;
int line = 0;
int err = 0;
struct msg *curr = NULL;
int ncurr = 0;
char buf[BUFSZ];
FILE* f;
f = dfopen(MSGDB_PATH, fname, "r");
if (f == NULL) {
perror(fname);
exit(EX_NOINPUT);
}
msg_table[lang] = alloc_perm(nmsgid * sizeof(**msg_table));
memset(msg_table[lang], 0, nmsgid * sizeof(**msg_table));
while (fgets(buf, sizeof(buf), f)) {
char* p;
static char WS[] = " \t";
/* strip trailing '\n' and check if line is too long */
line++;
p = strchr(buf, '\n');
if (p == NULL) {
fprintf(stderr, "%s:%d: line too long\n", fname, line);
exit(EX_DATAERR);
}
else
*p = '\0';
/* skip leading spaces */
for (p = buf; *p && strchr(WS, *p) != NULL; p++);
if (*p == '\0')
continue;
/* skip comment lines */
if (*p == '#')
continue;
if (isalpha(*p)) {
char* name;
int msgid;
name = strsep(&p, WS);
if ((msgid = msgid_lookup(name)) < 0) {
fprintf(stderr, "%s:%d: '%s': unknown "
"identifier\n",
fname, line, name);
exit(EX_DATAERR);
}
curr = msg_table[lang] + msgid;
if (curr->p != NULL) {
fprintf(stderr, "%s:%d: '%s' redefined\n",
fname, line, name);
exit(EX_DATAERR);
}
if (p != NULL) {
while (*p && strchr(WS, *p) != NULL)
p++;
if (*p == '\0')
p = NULL;
}
if (p == NULL || *p == '"') {
curr->sexdep = 0;
curr->p = NULL;
}
else {
char* depname;
depname = strsep(&p, WS);
if (strcmp(depname, "sexdep") == 0)
curr->sexdep = DEP_CHAR;
else if (strcmp(depname, "vsexdep") == 0)
curr->sexdep = DEP_VICTIM;
else {
fprintf(stderr,
"%s:%d: invalid "
"sex dependancy\n",
fname, line);
exit(EX_DATAERR);
}
if (p != NULL) {
while (*p && strchr(WS, *p) != NULL)
p++;
if (*p == '\0')
p = NULL;
}
ncurr = 0;
curr->p = alloc_perm(SEX_MAX*sizeof(char*));
for (i = 0; i < SEX_MAX; i++)
curr->p[i] = NULL;
}
if (p == NULL)
continue;
}
if (*p == '"') {
char* q;
if (curr == NULL
|| (curr->sexdep == 0 && curr->p)
|| (curr->sexdep && ncurr >= SEX_MAX)) {
fprintf(stderr, "%s:%d: unexpected msg\n",
fname, line);
exit(EX_DATAERR);
}
for(q = p+1; *q; q++)
if (*q == '\\') {
int c;
if (*++q == '\0')
break;
switch (*q) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 'a':
c = '\a';
break;
default:
c = '\0';
break;
}
if (c) {
memcpy(q, q+1, strlen(q+1) + 1);
*--q = c;
}
continue;
}
else if (*q == '"')
break;
if (*q == '\0') {
fprintf(stderr, "%s:%d: missing '\"' \n",
fname, line);
exit(EX_DATAERR);
}
*q = '\0';
q = str_dup(p+1);
if (curr->sexdep)
curr->p[ncurr++] = q;
else
(char*) curr->p = q;
}
else {
fprintf(stderr, "%s:%d: syntax error\n", fname, line);
exit(EX_DATAERR);
}
}
for (i = 0; i < nmsgid; i++) {
int undefined = 0;
struct msg *m = msg_table[lang] + i;
if (m->sexdep) {
int j;
for (j = 0; j < SEX_MAX; j++)
if (m->p[j] == NULL) {
undefined = 1;
break;
}
}
else if (m->p == NULL)
undefined = 1;
if (undefined) {
fprintf(stderr, "%s: %s undefined\n",
fname, msgid_name_lookup(i));
err = 1;
}
}
fclose(f);
if (err)
exit(EX_DATAERR);
}
static
int
msgid_cmp(const void* p1, const void* p2)
{
return strcmp(*(char**)p1, *(char**)p2);
}
static
int
msgid_lookup(char* name)
{
struct msgid *m;
m = bsearch(&name, msgid_table, nmsgid, sizeof(*msgid_table),
msgid_cmp);
if (m == NULL)
return -1;
return m->msgid;
}
static
void
msgid_add(char* name, int msgid)
{
if (nmsgid == nalloc) {
struct msgid *m;
nalloc += MSGID_ALLOC_STEP;
m = realloc(msgid_table, nalloc * sizeof(*msgid_table));
if (m == NULL) {
perror("msgid_add");
exit(EX_OSERR);
}
msgid_table = m;
}
msgid_table[nmsgid].name = str_dup(name);
msgid_table[nmsgid].msgid = msgid;
nmsgid++;
}
static
const char*
msgid_name_lookup(int msgid)
{
int i;
for (i = 0; i < nmsgid; i++)
if (msgid_table[i].msgid == msgid)
return msgid_table[i].name;
return NULL;
}