muddy/
muddy/CVS/
muddy/area/
muddy/area/CVS/
muddy/clans/CVS/
muddy/classes/CVS/
muddy/doc/
muddy/doc/CVS/
muddy/etc/CVS/
muddy/etc/i3/
muddy/etc/i3/CVS/
muddy/imc/CVS/
muddy/lang/CVS/
muddy/licenses/CVS/
muddy/msgdb/CVS/
muddy/new/CVS/
muddy/notes/
muddy/player/
muddy/races/CVS/
muddy/religions/CVS/
muddy/src/CVS/
muddy/src/comm/CVS/
muddy/src/db/CVS/
muddy/src/intermud/
muddy/src/intermud/CVS/
muddy/src/irc/CVS/
muddy/src/olc/CVS/
/* $Id: mlstring.c,v 1.666 2004/09/20 10:49:51 shrike Exp $                             */

/************************************************************************************
 *    Copyright 2004 Astrum Metaphora consortium                                    *
 *                                                                                  *
 *    Licensed under the Apache License, Version 2.0 (the "License");               *
 *    you may not use this file except in compliance with the License.              *
 *    You may obtain a copy of the License at                                       *
 *                                                                                  *
 *    http://www.apache.org/licenses/LICENSE-2.0                                    *
 *                                                                                  *
 *    Unless required by applicable law or agreed to in writing, software           *
 *    distributed under the License is distributed on an "AS IS" BASIS,             *
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.      *
 *    See the License for the specific language governing permissions and           *
 *    limitations under the License.                                                *
 *                                                                                  *
 ************************************************************************************/
 /************************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR           *
 *     ANATOLIA has been brought to you by ANATOLIA consortium                      *
 *       Serdar BULUT {Chronos}         bulut@rorqual.cc.metu.edu.tr                *
 *       Ibrahim Canpunar  {Asena}      canpunar@rorqual.cc.metu.edu.tr             *
 *       Murat BICER  {KIO}             mbicer@rorqual.cc.metu.edu.tr               *
 *       D.Baris ACAR {Powerman}        dbacar@rorqual.cc.metu.edu.tr               *
 *     By using this code, you have agreed to follow the terms of the               *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence                      *
 ***********************************************************************************/

/************************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,                 *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.            *
 *                                                                                  *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael                   *
 *  Chastain, Michael Quan, and Mitchell Tse.                                       *
 *                                                                                  *
 *  In order to use any part of this Merc Diku Mud, you must comply with            *
 *  both the original Diku license in 'license.doc' as well the Merc                *
 *  license in 'license.txt'.  In particular, you may not remove either of          *
 *  these copyright notices.                                                        *
 *                                                                                  *
 *  Much time and thought has gone into this software and you are                   *
 *  benefitting.  We hope that you share your changes too.  What goes               *
 *  around, comes around.                                                           *
 ************************************************************************************/

/************************************************************************************
*       ROM 2.4 is copyright 1993-1995 Russ Taylor                                  *
*       ROM has been brought to you by the ROM consortium                           *
*           Russ Taylor (rtaylor@pacinfo.com)                                       *
*           Gabrielle Taylor (gtaylor@pacinfo.com)                                  *
*           Brian Moore (rom@rom.efn.org)                                           *
*       By using this code, you have agreed to follow the terms of the              *
*       ROM license, in the file Rom24/doc/rom.license                              *
*************************************************************************************/

/************************************************************************************
 * 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.                                                                     *
 ************************************************************************************/


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "merc.h"
#include "db/db.h"
#include "db/lang.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "merc.h"
#include "db/db.h"
#include "db/lang.h"

/*
 * multi-language string implementation
 */

/*
 * multi-language string
 * if nlang == 0 the value is stored in u.str
 * otherwise the value is stored in array of strings u.lstr
 * the size of array is equal to 'nlang'
 * 'ref' = number of references (COW semantics)
 */
struct mlstring {
	union {
		const char* str;
		const char** lstr;
	} u;
	int nlang;
	int ref;
};

static const char* smash_a(const char *s, int len);
static char* fix_mlstring(const char* s);
static mlstring *mlstr_split(mlstring *ml);

int mlstr_count;
int mlstr_real_count;

mlstring mlstr_empty;

mlstring *mlstr_new(const char *mval)
{
	mlstring *res = malloc(sizeof(*res));
	res->u.str = str_dup(mval);
	res->nlang = 0;
	res->ref = 1;
	mlstr_real_count++;
	mlstr_count++;		
	return res;
}

mlstring *mlstr_fread(FILE *fp)
{
	const char *p;
	const char *s;
	int lang;
	mlstring *res;

	p = fread_string(fp);
	if (IS_NULLSTR(p))
		return &mlstr_empty;

//	mlstr_count++;
	res = mlstr_new(NULL);
	if (*p != '@' || *(p+1) == '@') {
		res->u.str = smash_a(p, -1);
		free_string(p);
		return res;
	}

	res->u.lstr = calloc(1, sizeof(char*) * langs.nused);
	res->nlang = langs.nused;

	s = p+1;
	for (;;) {
		const char *q;

		/* s points at lang id */
		q = strchr(s, ' ');
		if (q == NULL) {
			db_error("mlstr_fread", "no ` ' after `@' found");
			return res;
		}

		if ((lang = lang_nlookup(s, q-s)) < 0) {
			db_error("mlstr_fread", "%s: unknown language", s);
			return res;
		}

		if (res->u.lstr[lang] != NULL) {
			db_error("mlstr_fread", "lang %s: redefined", s);
			return res;
		}

		/* find next '@', skip "@@" */
		for (s = ++q; (s = strchr(s, '@'));) {
			if (*(s+1) != '@')
				break;
			s += 2;
		}
		if (s == NULL)
			s = strchr(q, '\0');
		res->u.lstr[lang] = smash_a(q, s-q);
		if (!*s++)
			break;
	}

	free_string(p);
	return res;
}

void mlstr_fwrite(FILE *fp, const char* name, const mlstring *ml)
{
	int lang;

	if (name)
		fprintf(fp, "%s ", name);

	if (!ml) {
		fprintf(fp, "~\n");
		return;
	}

	if (ml->nlang == 0) {
		fprintf(fp, "%s~\n", fix_mlstring(ml->u.str));
		return;
	}

	for (lang = 0; lang < ml->nlang && lang < langs.nused; lang++) {
		const char* p = ml->u.lstr[lang];
		lang_t *l;

		if (IS_NULLSTR(p))
			continue;

		l = VARR_GET(&langs, lang);
		fprintf(fp, "@%s %s", l->name, fix_mlstring(p));
	}
	fputs("~\n", fp);
}

void mlstr_free(mlstring *ml)
{
	if (ml == NULL || ml == &mlstr_empty)
		return;

	mlstr_count--;
	if (ml->ref < 1 || --ml->ref)
		return;

	if (ml->nlang == 0)
		free_string(ml->u.str);
	else {
		int lang;

		for (lang = 0; lang < ml->nlang; lang++)
			free_string(ml->u.lstr[lang]);
		free(ml->u.lstr);
	}
	free(ml);
	mlstr_real_count--;
}

mlstring *mlstr_dup(mlstring *ml)
{
	if (ml == NULL)
		return NULL;
	mlstr_count++;
	if (ml != &mlstr_empty)
		ml->ref++;
	return ml;
}

mlstring *mlstr_printf(mlstring *ml,...)
{
	char buf[MAX_STRING_LENGTH];
	va_list ap;
	mlstring *res;

	if (ml == NULL)
		return NULL;

//	mlstr_count++;
	res = mlstr_new(NULL);
	res->nlang = ml->nlang;

	va_start(ap, ml);
	if (ml->nlang == 0) {
		vsnprintf(buf, sizeof(buf), ml->u.str, ap);
		res->u.str = str_dup(buf);
	}
	else {
		int lang;

		res->u.lstr = calloc(1, sizeof(char*) * res->nlang);
		for (lang = 0; lang < ml->nlang; lang++) {
			if (IS_NULLSTR(ml->u.lstr[lang]))
				continue;
			vsnprintf(buf, sizeof(buf), ml->u.lstr[lang], ap);
			res->u.lstr[lang] = str_dup(buf);
		}
	}
	va_end(ap);
	return res;
}

int mlstr_nlang(const mlstring *ml)
{
	if (ml == NULL)
		return 0;
	return ml->nlang;
}

const char * mlstr_val(const mlstring *ml, int lang)
{
	const char *p;

	if (ml == NULL)
		return str_empty;

	if (ml->nlang == 0) {
		p = ml->u.str;
		return (p ? (p[0] == '^' ? p + 1 : p) : str_empty);
	}

	if (lang >= ml->nlang
	||  lang < 0
	||  IS_NULLSTR(ml->u.lstr[lang])) {
		lang_t *l;

		if ((l = varr_get(&langs, lang))
		&&  l->slang_of >= 0
		&&  l->slang_of < ml->nlang)
			lang = l->slang_of;
		else
			return str_empty;
	}

	p = ml->u.lstr[lang];
	return (p ? (p[0] == '^' ? p + 1 : p) : str_empty);
}

bool mlstr_null(const mlstring *ml)
{
	const char *mval = mlstr_mval(ml);
	return (mval == NULL) || (*mval == '\0');
}

int mlstr_cmp(const mlstring *ml1, const mlstring *ml2)
{
	int lang;
	int res;

	if (ml1 == NULL)
		if (ml2 == NULL)
			return 0;
		else
			return 1;
	else if (ml2 == NULL)
		return -1;

	if (ml1->nlang != ml2->nlang)
		return  ml1->nlang - ml2->nlang;

	if (ml1->nlang == 0)
		return str_cmp(ml1->u.str, ml2->u.str);

	for (lang = 0; lang < ml1->nlang; lang++) {
		res = str_cmp(ml1->u.lstr[lang], ml2->u.lstr[lang]);
		if (res)
			return res;
	}

	return 0;
}

const char** mlstr_convert (mlstring **mlp, int newlang)
{
	const char *old;
	int lang;

	*mlp = mlstr_split (*mlp);
	if (newlang < 0) {
		/* convert to language-independent */
		if ((*mlp)->nlang) {
			old = (*mlp)->u.lstr [0];
			
			for (lang = 1 ; lang < (*mlp)->nlang ; lang++)
				free_string ((*mlp)->u.lstr[lang]);
			free ((*mlp)->u.lstr);
			(*mlp)->nlang = 0;
			(*mlp)->u.str = old;
		}
		return &((*mlp)->u.str);
	}

	/* convert to language-dependent */
	if ((*mlp)->nlang == 0) {
		old = (*mlp)->u.str;
		(*mlp)->nlang = langs.nused;
		(*mlp)->u.lstr = calloc(1, sizeof (char*) * langs.nused);
		
		(*mlp)->u.lstr [0] = old;
		
		for (lang = 1 ; lang < langs.nused ; lang++) {
		    (*mlp)->u.lstr [lang] = str_dup (old);
		}
		
		//if (newlang > 0) (*mlp)->u.lstr[newlang] = str_dup (old) ;
	}
	return ((*mlp)->u.lstr) + newlang;
}

bool mlstr_append(CHAR_DATA *ch, mlstring **mlp, const char *arg)
{
	int lang;

	lang = lang_lookup(arg);
	if (lang < 0 && str_cmp(arg, "all"))
		return FALSE;

	string_append(ch, mlstr_convert(mlp, lang));
	return TRUE;
}

void mlstr_for_each(mlstring **mlp, void *arg,
		    void (*cb)(int lang, const char **p, void *arg))
{
	int lang;

	if (*mlp == NULL)
		return;

	*mlp = mlstr_split(*mlp);
	if ((*mlp)->nlang == 0) {
		cb(0, &((*mlp)->u.str), arg);
		return;
	}

	for (lang = 0; lang < (*mlp)->nlang; lang++)
		cb(lang, (*mlp)->u.lstr + lang, arg);
}

bool mlstr_edit(mlstring **mlp, const char *argument)
{
	char arg[MAX_STRING_LENGTH];
	int lang;
	const char **p;

	argument = one_argument(argument, arg, sizeof(arg));
	lang = lang_lookup(arg);
	if (lang < 0 && str_cmp(arg, "all"))
		return FALSE;

	p = mlstr_convert(mlp, lang);
	free_string(*p);
	*p = str_dup(argument);
	return TRUE;
}

/*
 * The same as mlstr_edit, but '\n' is appended.
 */
bool mlstr_editnl(mlstring **mlp, const char *argument)
{
	char arg[MAX_STRING_LENGTH];
	int lang;
	const char **p;

	argument = one_argument(argument, arg, sizeof(arg));
	lang = lang_lookup(arg);
	if (lang < 0 && str_cmp(arg, "all"))
		return FALSE;

	p = mlstr_convert(mlp, lang);
	free_string(*p);
	*p = str_printf("%s\n", argument);
	return TRUE;
}

void mlstr_dump(BUFFER *buf, const char *name, const mlstring *ml)
{
	char space[MAX_STRING_LENGTH];
	size_t namelen;
	int lang;
	static char FORMAT[] = "%s[%s] [%s]\n";
	lang_t *l;

	if (ml == NULL || ml->nlang == 0) {
		buf_printf(buf, FORMAT, name, "all",
			   ml == NULL ? "(null)" : ml->u.str);
		return;
	}

	if (langs.nused == 0)
		return;

	l = VARR_GET(&langs, 0);
	buf_printf(buf, FORMAT, name, l->name, ml->u.lstr[0]);

	if (langs.nused < 1)
		return;

	namelen = strlen(name);
	namelen = URANGE(0, namelen, sizeof(space)-1);
	memset(space, ' ', namelen);
	space[namelen] = '\0';

	for (lang = 1; lang < ml->nlang && lang < langs.nused; lang++) {
		l = VARR_GET(&langs, lang);
		buf_printf(buf, FORMAT,
			   space, l->name, ml->u.lstr[lang]);
	}
}

static const char *smash_a(const char *s, int len)
{
	char buf[MAX_STRING_LENGTH];
	char *p = buf;

	if (len < 0 || len > sizeof(buf)-1)
		len = sizeof(buf)-1;

	while (p-buf < len && *s) {
		if (*s == '@' && *(s+1) == '@')
			s++;
		*p++ = *s++;
	}
	*p = '\0';
	return str_dup(buf);
}

static char *fix_mlstring(const char *s)
{
	char *p;
	static char buf[MAX_STRING_LENGTH*2];

	buf[0] = '\0';

	if (s == NULL)
		return buf;

	s = fix_string(s);
	while((p = strchr(s, '@')) != NULL) {
		*p = '\0';
		strnzcat(buf, sizeof(buf), s);
		strnzcat(buf, sizeof(buf), "@@");
		s = p+1;
	}
	strnzcat(buf, sizeof(buf), s);
	return buf;
}

static mlstring *mlstr_split(mlstring *ml)
{
	int lang;
	mlstring *res;

	if (ml != NULL && ml != &mlstr_empty && ml->ref < 2) 
		return ml;

	res = mlstr_new(NULL);

	if (ml == NULL || ml == &mlstr_empty) 
		return res;

	res->nlang = ml->nlang;
	ml->ref--;
	if (ml->nlang == 0) {
		res->u.str = str_qdup(ml->u.str);
		return res;
	}

	res->u.lstr = malloc(sizeof(char*) * res->nlang);
	for (lang = 0; lang < res->nlang; lang++)
		res->u.lstr[lang] = str_qdup(ml->u.lstr[lang]);
	return res;
}

/* new functions */
mlstring *mlstr_addstr(mlstring *ml, const char *s)
{
	mlstring *res;
	int lang;
	char tmp[MAX_STRING_LENGTH];

	if (s == NULL) {
		res = mlstr_dup(ml);
		return res;
	}

	res = mlstr_new(NULL);
	if (ml->nlang == 0) {
		strnzcpy(tmp, sizeof(tmp), ml->u.str);
		strnzcat(tmp, sizeof(tmp), s);
		res->u.str = str_dup(tmp);
	}
	else {
		res->u.lstr = malloc(sizeof(char*) * ml->nlang);
       	for (lang = 0; lang < ml->nlang; lang++)
			if (ml->u.lstr[lang] != NULL) {
				strnzcpy(tmp, sizeof(tmp), ml->u.lstr[lang]);
				strnzcat(tmp, sizeof(tmp), s);
				res->u.lstr[lang] = str_dup(tmp);
			}
		else 
			res->u.lstr[lang] = str_empty;
	}
	res->nlang = ml->nlang;
	return res;

}

mlstring *mlstr_addmlstr(mlstring *ml1, mlstring *ml2)
{
	mlstring *res;
	int lang;
	char tmp[MAX_STRING_LENGTH];

	if (mlstr_null(ml1)) {
		if (mlstr_null(ml2))
			return &mlstr_empty;
		else
			return mlstr_dup(ml2);
	}
	if (mlstr_null(ml2))
		return mlstr_dup(ml1);

	if (ml2->nlang == 0)
		return mlstr_addstr(ml1, ml2->u.str);

	if (ml1->nlang == 0) {
		res = mlstr_new(NULL);
		res->nlang = ml2->nlang;
		res->u.lstr = malloc(sizeof(char*) * ml2->nlang);
        for (lang = 0; lang < ml2->nlang; lang++)
            if (ml2->u.lstr[lang] != NULL && ml1->u.str != NULL) {
			    strnzcpy(tmp, sizeof(tmp), ml1->u.str);
                strnzcat(tmp, sizeof(tmp), ml2->u.lstr[lang]);
                res->u.lstr[lang] = str_dup(tmp);
            }
            else 
				res->u.lstr[lang] = str_empty;
		return res;
	}

	if (ml1->nlang != ml2->nlang)
		return mlstr_addstr(ml1, mlstr_val(ml2, 0));

	res = mlstr_new(NULL);
	res->nlang = ml1->nlang;
	res->u.lstr = malloc(sizeof(char*) * ml1->nlang);
	for (lang = 0; lang < ml1->nlang; lang++)
		if (ml1->u.lstr[lang] == NULL) {
			if (ml2->u.lstr[lang] == NULL)
				res->u.lstr[lang] = str_empty;
			else res->u.lstr[lang] = str_dup(ml2->u.lstr[lang]);
		}
		else if (ml2->u.lstr[lang] == NULL)
			res->u.lstr[lang] = str_dup(ml1->u.lstr[lang]);
		else {
			strnzcpy(tmp, sizeof(tmp), ml1->u.lstr[lang]);
			strnzcat(tmp, sizeof(tmp), ml2->u.lstr[lang]);
			res->u.lstr[lang] = str_dup(tmp);
		}

	return res;
}

#ifndef XORADEAD
const char * mlstr_val_true(const mlstring *ml, int lang)
{
	const char *p;

	if (ml == NULL)
		return str_empty;

	if (ml->nlang == 0) {
		p = ml->u.str;
		return (p ? p : str_empty);
	}

	if (lang >= ml->nlang
	||  lang < 0
	||  IS_NULLSTR(ml->u.lstr[lang])) {
		lang_t *l;

		if ((l = varr_get(&langs, lang))
		&&  l->slang_of >= 0
		&&  l->slang_of < ml->nlang)
			lang = l->slang_of;
		else
			return str_empty;
	}

	p = ml->u.lstr[lang];
	return (p ? p : str_empty);
}
#endif