fbmuck-6.05/auto/
fbmuck-6.05/contrib/jresolver/
fbmuck-6.05/contrib/jresolver/org/
fbmuck-6.05/contrib/jresolver/org/fuzzball/
fbmuck-6.05/docs/devel/
fbmuck-6.05/game/
fbmuck-6.05/game/logs/
fbmuck-6.05/game/muf/
fbmuck-6.05/scripts/
fbmuck-6.05/src_docs/
/* $Header: /cvsroot/fbmuck/fbmuck/src/stringutil.c,v 1.24 2004/07/11 15:12:47 sombre Exp $ */


#include "copyright.h"
#include "config.h"
#include "db.h"
#include "tune.h"
#include "props.h"
#include "params.h"
#include "interface.h"
#include "mpi.h"

/* String utilities */

#include <stdio.h>
#include <ctype.h>
#include "externs.h"

#define DOWNCASE(x) (tolower(x))

/*
 * routine to be used instead of strcasecmp() in a sorting routine
 * Sorts alphabetically or numerically as appropriate.
 * This would compare "network100" as greater than "network23"
 * Will compare "network007" as less than "network07"
 * Will compare "network23a" as less than "network23b"
 * Takes same params and returns same comparitive values as strcasecmp.
 * This ignores minus signs and is case insensitive.
 */
int
alphanum_compare(const char *t1, const char *s2)
{
	int n1, n2, cnt1, cnt2;
	const char *u1, *u2;
	register const char *s1 = t1;

	while (*s1 && DOWNCASE(*s1) == DOWNCASE(*s2))
		s1++, s2++;

	/* if at a digit, compare number values instead of letters. */
	if (isdigit(*s1) && isdigit(*s2)) {
		u1 = s1;
		u2 = s2;
		n1 = n2 = 0;			/* clear number values */
		cnt1 = cnt2 = 0;

		/* back up before zeros */
		if (s1 > t1 && *s2 == '0')
			s1--, s2--;			/* curr chars are diff */
		while (s1 > t1 && *s1 == '0')
			s1--, s2--;			/* prev chars are same */
		if (!isdigit(*s1))
			s1++, s2++;

		/* calculate number values */
		while (isdigit(*s1))
			cnt1++, n1 = (n1 * 10) + (*s1++ - '0');
		while (isdigit(*s2))
			cnt2++, n2 = (n2 * 10) + (*s2++ - '0');

		/* if more digits than int can handle... */
		if (cnt1 > 8 || cnt2 > 8) {
			if (cnt1 == cnt2)
				return (*u1 - *u2);	/* cmp chars if mag same */
			return (cnt1 - cnt2);	/* compare magnitudes */
		}

		/* if number not same, return count difference */
		if (n1 && n2 && n1 != n2)
			return (n1 - n2);

		/* else, return difference of characters */
		return (*u1 - *u2);
	}
	/* if characters not digits, and not the same, return difference */
	return (DOWNCASE(*s1) - DOWNCASE(*s2));
}

int
string_compare(register const char *s1, register const char *s2)
{
	while (*s1 && DOWNCASE(*s1) == DOWNCASE(*s2))
		s1++, s2++;

	return (DOWNCASE(*s1) - DOWNCASE(*s2));
}

const char *
exit_prefix(register const char *string, register const char *prefix)
{
	const char *p;
	const char *s = string;

	while (*s) {
		p = prefix;
		string = s;
		while (*s && *p && DOWNCASE(*s) == DOWNCASE(*p)) {
			s++;
			p++;
		}
		while (*s && isspace(*s))
			s++;
		if (!*p && (!*s || *s == EXIT_DELIMITER)) {
			return string;
		}
		while (*s && (*s != EXIT_DELIMITER))
			s++;
		if (*s)
			s++;
		while (*s && isspace(*s))
			s++;
	}
	return 0;
}

int
string_prefix(register const char *string, register const char *prefix)
{
	while (*string && *prefix && DOWNCASE(*string) == DOWNCASE(*prefix))
		string++, prefix++;
	return *prefix == '\0';
}

/* accepts only nonempty matches starting at the beginning of a word */
const char *
string_match(register const char *src, register const char *sub)
{
	if (*sub != '\0') {
		while (*src) {
			if (string_prefix(src, sub))
				return src;
			/* else scan to beginning of next word */
			while (*src && isalnum(*src))
				src++;
			while (*src && !isalnum(*src))
				src++;
		}
	}
	return 0;
}

#define GENDER_UNASSIGNED   0x0	/* unassigned - the default */
#define GENDER_NEUTER       0x1	/* neuter */
#define GENDER_FEMALE       0x2	/* for women */
#define GENDER_MALE         0x3	/* for men */
#define GENDER_HERM         0x4	/* for hermaphrodites */

/*
 * pronoun_substitute()
 *
 * %-type substitutions for pronouns
 *
 * %a/%A for absolute possessive (his/hers/hirs/its, His/Hers/Hirs/Its)
 * %s/%S for subjective pronouns (he/she/sie/it, He/She/Sie/It)
 * %o/%O for objective pronouns (him/her/hir/it, Him/Her/Hir/It)
 * %p/%P for possessive pronouns (his/her/hir/its, His/Her/Hir/Its)
 * %r/%R for reflexive pronouns (himself/herself/hirself/itself,
 *                                Himself/Herself/Hirself/Itself)
 * %n    for the player's name.
 */
char *
pronoun_substitute(int descr, dbref player, const char *str)
{
	char c;
	char d;
	char prn[3];
	char globprop[128];
	static char buf[BUFFER_LEN * 2];
	char orig[BUFFER_LEN];
	char sexbuf[BUFFER_LEN];
	char *result;
	const char *sexstr;
	const char *self_sub;		/* self substitution code */
	const char *temp_sub;
	dbref mywhere = player;
	int sex;

	static const char *subjective[5] = { "", "it", "she", "he", "sie" };
	static const char *possessive[5] = { "", "its", "her", "his", "hir" };
	static const char *objective[5] = { "", "it", "her", "him", "hir" };
	static const char *reflexive[5] = { "", "itself", "herself", "himself", "hirself" };
	static const char *absolute[5] = { "", "its", "hers", "his", "hirs" };

	prn[0] = '%';
	prn[2] = '\0';

	str = uncompress(str);

	strcpy(orig, str);
	str = orig;

	sexstr = get_property_class(player, "sex");
	if (sexstr) {
		sexstr = do_parse_mesg(descr, player, player, sexstr, "(Lock)", sexbuf,
						(MPI_ISPRIVATE | MPI_ISLOCK |
							(Prop_Blessed(player, "sex")? MPI_ISBLESSED : 0)));
	}
	while (sexstr && isspace(*sexstr)) sexstr++;

	sex = GENDER_UNASSIGNED;

	if (!sexstr || !*sexstr) {
		sexstr = "_default";
	}
	else
	{
		char* last_non_space = sexbuf;
		char* ptr = sexbuf;

		for(; *ptr; ptr++)
			if (!isspace(*ptr))
				last_non_space = ptr;
		
		if (*last_non_space)
			*(last_non_space + 1) = '\0';

		if (string_compare(sexstr, "male") == 0)
			sex = GENDER_MALE;
		else if (string_compare(sexstr, "female") == 0)
			sex = GENDER_FEMALE;
		else if (string_compare(sexstr, "hermaphrodite") == 0)
			sex = GENDER_HERM;
		else if (string_compare(sexstr, "herm") == 0)
			sex = GENDER_HERM;
		else if (string_compare(sexstr, "neuter") == 0)
			sex = GENDER_NEUTER;
	}

	result = buf;
	while (*str) {
		if (*str == '%') {
			*result = '\0';
			prn[1] = c = *(++str);
			if (!c) {
				*(result++) = '%';
				continue;
			} else if (c == '%') {
				*(result++) = '%';
				str++;
			} else {
				mywhere = player;
				d = (isupper(c)) ? c : toupper(c);

				snprintf(globprop, sizeof(globprop), "_pronouns/%.64s/%s", uncompress(sexstr), prn);
				if (d == 'A' || d == 'S' || d == 'O' || d == 'P' || d == 'R' || d == 'N') {
					self_sub = uncompress(get_property_class(mywhere, prn));
				} else {
					self_sub = uncompress(envpropstr(&mywhere, prn));
				}
				if (!self_sub) {
					self_sub = uncompress(get_property_class(player, globprop));
				}
				if (!self_sub) {
					self_sub = uncompress(get_property_class(0, globprop));
				}
				if (!self_sub && (sex == GENDER_UNASSIGNED)) {
					snprintf(globprop, sizeof(globprop), "_pronouns/_default/%s", prn);

					if (!(self_sub = uncompress(get_property_class(player, globprop))))
						self_sub = uncompress(get_property_class(0, globprop));
				}

				if (self_sub) {
					temp_sub = NULL;
					if (self_sub[0] == '%' && toupper(self_sub[1]) == 'N') {
						temp_sub = self_sub;
						self_sub = NAME(player);
					}
					if (((result - buf) + strlen(self_sub)) > (BUFFER_LEN - 2))
						return buf;
					strcatn(result, sizeof(buf) - (result - buf), self_sub);
					if (isupper(prn[1]) && islower(*result))
						*result = toupper(*result);
					result += strlen(result);
					str++;
					if (temp_sub) {
						if (((result - buf) + strlen(temp_sub+2)) > (BUFFER_LEN - 2))
							return buf;
						strcatn(result, sizeof(buf) - (result - buf), temp_sub+2);
						if (isupper(temp_sub[1]) && islower(*result))
							*result = toupper(*result);
						result += strlen(result);
						str++;
					}
				} else if (sex == GENDER_UNASSIGNED) {
					switch (c) {
					case 'n':
					case 'N':
					case 'o':
					case 'O':
					case 's':
					case 'S':
					case 'r':
					case 'R':
						strcatn(result, sizeof(buf) - (result - buf), NAME(player));
						break;
					case 'a':
					case 'A':
					case 'p':
					case 'P':
						strcatn(result, sizeof(buf) - (result - buf), NAME(player));
						strcatn(result, sizeof(buf) - (result - buf), "'s");
						break;
					default:
						result[0] = *str;
						result[1] = 0;
						break;
					}
					str++;
					result += strlen(result);
					if ((result - buf) > (BUFFER_LEN - 2)) {
						buf[BUFFER_LEN - 1] = '\0';
						return buf;
					}
				} else {
					switch (c) {
					case 'a':
					case 'A':
						strcatn(result, sizeof(buf) - (result - buf), absolute[sex]);
						break;
					case 's':
					case 'S':
						strcatn(result, sizeof(buf) - (result - buf), subjective[sex]);
						break;
					case 'p':
					case 'P':
						strcatn(result, sizeof(buf) - (result - buf), possessive[sex]);
						break;
					case 'o':
					case 'O':
						strcatn(result, sizeof(buf) - (result - buf), objective[sex]);
						break;
					case 'r':
					case 'R':
						strcatn(result, sizeof(buf) - (result - buf), reflexive[sex]);
						break;
					case 'n':
					case 'N':
						strcatn(result, sizeof(buf) - (result - buf), NAME(player));
						break;
					default:
						*result = *str;
						result[1] = '\0';
						break;
					}
					if (isupper(c) && islower(*result)) {
						*result = toupper(*result);
					}
					result += strlen(result);
					str++;
					if ((result - buf) > (BUFFER_LEN - 2)) {
						buf[BUFFER_LEN - 1] = '\0';
						return buf;
					}
				}
			}
		} else {
			if ((result - buf) > (BUFFER_LEN - 2)) {
				buf[BUFFER_LEN - 1] = '\0';
				return buf;
			}
			*result++ = *str++;
		}
	}
	*result = '\0';
	return buf;
}

#ifndef MALLOC_PROFILING

char *
alloc_string(const char *string)
{
	char *s;

	/* NULL, "" -> NULL */
	if (!string || !*string)
		return 0;

	if ((s = (char *) malloc(strlen(string) + 1)) == 0) {
		abort();
	}
	strcpy(s, string);
	return s;
}

struct shared_string *
alloc_prog_string(const char *s)
{
	struct shared_string *ss;
	int length;

	if (s == NULL || *s == '\0')
		return (NULL);

	length = strlen(s);
	if ((ss = (struct shared_string *)
		 malloc(sizeof(struct shared_string) + length)) == NULL)
		abort();

	ss->links = 1;
	ss->length = length;
	bcopy(s, ss->data, ss->length + 1);
	return (ss);
}


char *
string_dup(const char *s)
{
	char *p;

	p = (char *) malloc(1 + strlen(s));
	if (p)
		(void) strcpy(p, s);
	return (p);
}
#endif



char *
intostr(int i)
{
	static char num[16];
	int j, k;
	char *ptr2;

	k = i;
	ptr2 = num + 14;
	num[15] = '\0';
	if (i < 0)
		i = -i;
	while (i) {
		j = i % 10;
		*ptr2-- = '0' + j;
		i /= 10;
	}
	if (!k)
		*ptr2-- = '0';
	if (k < 0)
		*ptr2-- = '-';
	return (++ptr2);
}

/*
 * Encrypt one string with another one.
 */

#define CHARCOUNT 97

static char enarr[256];
static int charset_count[] = { 96, 97, 0 };
static int initialized_crypt = 0;

void
init_crypt(void)
{
	int i;

	for (i = 0; i <= 255; i++)
		enarr[i] = (char) i;
	for (i = 'A'; i <= 'M'; i++)
		enarr[i] = (char) enarr[i] + 13;
	for (i = 'N'; i <= 'Z'; i++)
		enarr[i] = (char) enarr[i] - 13;
	enarr['\r'] = 127;
	enarr[127] = '\r';
	enarr[ESCAPE_CHAR] = 31;
	enarr[31] = ESCAPE_CHAR;
	initialized_crypt = 1;
}


const char *
strencrypt(const char *data, const char *key)
{
	static char linebuf[BUFFER_LEN];
	char buf[BUFFER_LEN + 8];
	const char *cp;
	char *ptr;
	char *ups;
	const char *upt;
	int linelen;
	int count;
	int seed, seed2, seed3;
	int limit = BUFFER_LEN;
	int result;

	if (!initialized_crypt)
		init_crypt();

	seed = 0;
	for (cp = key; *cp; cp++)
		seed = ((((*cp) ^ seed) + 170) % 192);

	seed2 = 0;
	for (cp = data; *cp; cp++)
		seed2 = ((((*cp) ^ seed2) + 21) & 0xff);
	seed3 = seed2 = ((seed2 ^ (seed ^ (RANDOM() >> 24))) & 0x3f);

	count = seed + 11;
	for (upt = data, ups = buf, cp = key; *upt; upt++) {
		count = (((*cp) ^ count) + (seed ^ seed2)) & 0xff;
		seed2 = ((seed2 + 1) & 0x3f);
		if (!*(++cp))
			cp = key;
		result = (enarr[(unsigned char)*upt] - (32 - (CHARCOUNT - 96))) + count + seed;
		*ups = enarr[(result % CHARCOUNT) + (32 - (CHARCOUNT - 96))];
		count = (((*upt) ^ count) + seed) & 0xff;
		ups++;
	}
	*ups++ = '\0';

	ptr = linebuf;

	linelen = strlen(data);
	*ptr++ = (' ' + 2);
	*ptr++ = (' ' + seed3);
	limit--;
	limit--;

	for (cp = buf; cp < &buf[linelen]; cp++) {
		limit--;
		if (limit < 0)
			break;
		*ptr++ = *cp;
	}
	*ptr++ = '\0';
	return linebuf;
}



const char *
strdecrypt(const char *data, const char *key)
{
	char linebuf[BUFFER_LEN];
	static char buf[BUFFER_LEN];
	const char *cp;
	char *ptr;
	char *ups;
	const char *upt;
	int linelen;
	int outlen;
	int count;
	int seed, seed2;
	int result;
	int chrcnt;

	if (!initialized_crypt)
		init_crypt();

	ptr = linebuf;

	if ((data[0] - ' ') < 1 || (data[0] - ' ') > 2) {
		return "";
	}

	linelen = strlen(data);
	chrcnt = charset_count[(data[0] - ' ') - 1];
	seed2 = (data[1] - ' ');

	strcpy(linebuf, data + 2);

	seed = 0;
	for (cp = key; *cp; cp++) {
		seed = (((*cp) ^ seed) + 170) % 192;
	}

	count = seed + 11;
	outlen = strlen(linebuf);
	upt = linebuf;
	ups = buf;
	cp = key;
	while ((const char *) upt < &linebuf[outlen]) {
		count = (((*cp) ^ count) + (seed ^ seed2)) & 0xff;
		if (!*(++cp))
			cp = key;
		seed2 = ((seed2 + 1) & 0x3f);

		result = (enarr[(unsigned char)*upt] - (32 - (chrcnt - 96))) - (count + seed);
		while (result < 0)
			result += chrcnt;
		*ups = enarr[result + (32 - (chrcnt - 96))];

		count = (((*ups) ^ count) + seed) & 0xff;
		ups++;
		upt++;
	}
	*ups++ = '\0';

	return buf;
}


char *
strip_ansi(char *buf, const char *input)
{
	const char *is;
	char *os;

	buf[0] = '\0';
	os = buf;

	is = input;

	while (*is) {
		if (*is == ESCAPE_CHAR) {
			is++;
			if (*is == '[') {
				is++;
				while (isdigit(*is) || *is == ';')
					is++;
				if (*is == 'm')
					is++;
			} else {
				is++;
			}
		} else {
			*os++ = *is++;
		}
	}
	*os = '\0';

	return buf;
}


char *
strip_bad_ansi(char *buf, const char *input)
{
	const char *is;
	char *os;
	int aflag = 0;
	int limit = BUFFER_LEN - 5;

	buf[0] = '\0';
	os = buf;

	is = input;

	while (*is && limit-->0) {
		if (*is == ESCAPE_CHAR) {
			if (is[1] == '\0') {
				is++;
			} else if (is[1] != '[') {
				is++;
				is++;
			} else {
				aflag = 1;
				*os++ = *is++;	/* esc */
				*os++ = *is++;	/*  [  */
				while (isdigit(*is) || *is == ';') {
					*os++ = *is++;
				}
				if (*is != 'm') {
					*os++ = 'm';
				}
				*os++ = *is++;
			}
		} else {
			*os++ = *is++;
		}
	}
	if (aflag) {
		int termrn = 0;
		if (*(os - 2) == '\r' && *(os - 1) == '\n') {
			termrn = 1;
			os -= 2;
		}
		*os++ = '\033';
		*os++ = '[';
		*os++ = '0';
		*os++ = 'm';
		if (termrn) {
			*os++ = '\r';
			*os++ = '\n';
		}
	}
	*os = '\0';

	return buf;
}

/* Prepends what before before, granted it doesn't come
 * before start in which case it returns 0.
 * Otherwise it modifies *before to point to that new location,
 * and it returns the number of chars prepended.
 */
int
prepend_string(char** before, char* start, const char* what)
{
   char* ptr;
   size_t len;
   len = strlen(what);
   ptr = *before - len;
   if (ptr < start)
       return 0;
   memcpy((void*) ptr, (const void*) what, len);
   *before = ptr;
   return len;
}

int
is_valid_pose_separator(char ch)
{
	return (ch == '\'') || (ch == ' ') || (ch == ',') || (ch == '-');
}

void
prefix_message(char* Dest, const char* Src, const char* Prefix, int BufferLength, int SuppressIfPresent)
{
	int PrefixLength			= strlen(Prefix);
	int CheckForHangingEnter	= 0;

	while((BufferLength > PrefixLength) && (*Src != '\0'))
	{
		if (*Src == '\r')
		{
			Src++;
			continue;
		}

		if (!SuppressIfPresent || strncmp(Src, Prefix, PrefixLength) || (
				!is_valid_pose_separator(Src[PrefixLength]) &&	
				(Src[PrefixLength] != '\r') &&
				(Src[PrefixLength] != '\0')
			))
		{
			strcpy(Dest, Prefix);

			Dest			+= PrefixLength;
			BufferLength	-= PrefixLength;

			if (BufferLength > 1)
			{
				if (!is_valid_pose_separator(*Src))
				{
					*Dest++ = ' ';
					BufferLength--;
				}
			}
		}

		while((BufferLength > 1) && (*Src != '\0'))
		{
				*Dest++ = *Src;
				BufferLength--;

				if (*Src++ == '\r')
				{
					CheckForHangingEnter = 1;
					break;
				}
		}
	}

	if (CheckForHangingEnter && (Dest[-1] == '\r'))
		Dest--;

	*Dest = '\0';
}

int
is_prop_prefix(const char* Property, const char* Prefix)
{
	while(*Property == PROPDIR_DELIMITER)
		Property++;

	while(*Prefix == PROPDIR_DELIMITER)
		Prefix++;

	while(*Prefix)
	{
		if (*Property == '\0')
			return 0;

		if (*Property++ != *Prefix++)
			return 0;
	}

	return (*Property == '\0') || (*Property == PROPDIR_DELIMITER);
}

int
has_suffix(const char* text, const char* suffix)
{
	int tlen = text ? strlen(text) : 0;
	int slen = suffix ? strlen(suffix) : 0;

	if (!tlen || !slen || (tlen < slen))
		return 0;

	return !string_compare(text + tlen - slen, suffix);
}

int
has_suffix_char(const char* text, char suffix)
{
	int tlen = text ? strlen(text) : 0;

	if (tlen < 1)
		return 0;

	return text[tlen - 1] == suffix;
}


/*
 * Like strncpy, except it guarentees null termination of the result string.
 * It also has a more sensible argument ordering.
 */
char*
strcpyn(char* buf, size_t bufsize, const char* src)
{
	int pos = 0;
	char* dest = buf;

	while (++pos < bufsize && *src) {
		*dest++ = *src++;
	}
	*dest = '\0';
	return buf;
}


/*
 * Like strncat, except it takes the buffer size instead of the number
 * of characters to catenate.  It also has a more sensible argument order.
 */
char*
strcatn(char* buf, size_t bufsize, const char* src)
{
	int pos = strlen(buf);
	char* dest = &buf[pos];

	while (++pos < bufsize && *src) {
		*dest++ = *src++;
	}
	if (pos <= bufsize) {
		*dest = '\0';
	}
	return buf;
}