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