tinymush-3.0b21/game/bin/
tinymush-3.0b21/game/data/
tinymush-3.0b21/src/tools/
/*
 * stringutil.c -- string utilities 
 */
/*
 * $Id: stringutil.c,v 1.24 2000/01/08 21:08:25 cvs Exp $ 
 */

#include "copyright.h"
#include "autoconf.h"

#include "mudconf.h"
#include "config.h"
#include "externs.h"
#include "alloc.h"
#include "ansi.h"

/* Provide strtok_r (reentrant strtok) if needed */

#ifndef HAVE_STRTOK_R
char *strtok_r(s, sep, last)
char *s;
const char *sep;
char **last;
{
    if (!s)
	s = *last;
#ifdef HAVE_STRCSPN
    s += strspn(s, sep);
    *last = s + strcspn(s, sep);
#else /* HAVE_STRCSPN */
    while (strchr(sep, *s) && *s) s++;
    *last = s;
    while (!strchr(sep, **last) && **last) (*last)++;
#endif /* HAVE_STRCSPN */
    if (s == *last)
	return NULL;
    if (**last)
	*((*last)++) = '\0';
    return s;
}
#endif /* HAVE_STRTOK_R */

/* Convert raw character sequences into MUSH substitutions (type = 1)
 * or strips them (type = 0). */

char *ansi_numtab[I_ANSI_NUM] =
{
    "%xn", "%xh", 0,     0,     "%xu", "%xf", 0,     "%xi",
    0,     0,     0,     0,     0,     0,     0,     0,
    0,     0,     0,     0,     0,     0,     0,     0,
    0,     0,     0,     0,     0,     0,     "%xx", "%xr",
    "%xg", "%xy", "%xb", "%xm", "%xc", "%xw", 0,     0,
    "%xX", "%xR", "%xG", "%xY", "%xB", "%xM", "%xC", "%xW"
};

char *translate_string(str, type)
const char *str;
int type;
{
	char old[LBUF_SIZE];
	static char new[LBUF_SIZE];
	char *j, *c, *bp, *p;
	int i;

	bp = new;
	StringCopy(old, str);
		
	for (j = old; *j != '\0'; j++) {
	    switch (*j) {
		case ESC_CHAR:
		    c = (char *) index(j, ANSI_END);
		    if (c) {
			if (!type) {
			    j = c;
			    break;
			}
			*c = '\0';
			j += 2;
			
                       /* j points to the beginning of the string.
			* c points to the end of the string. Between
			* them is a set of numbers separated by
			* semicolons.
			*/

			do {
			    p = (char *) index(j, ';');
			    if (p)
				*p++ = '\0';
			    i = atoi(j);
			    if (i >= 0 && i < I_ANSI_NUM && ansi_numtab[i]) {
				safe_known_str(ansi_numtab[i], 3, new, &bp);
			    }
			    j = p;
			} while (p && *p);
			j = c;
		    } else {
			safe_chr(*j, new, &bp);
		    }
		    break;
		case ' ':
		    if ((*(j+1) == ' ') && type)
			safe_str("%b", new, &bp);
		    else 
			safe_chr(' ', new, &bp);
		    break;
		case '\\':
		    if (type)
			safe_str("%\\", new, &bp);
		    else
			safe_chr('\\', new, &bp);
		    break;
		case '%':
		    if (type)
			safe_str("%%", new, &bp);
		    else
			safe_chr('%', new, &bp);
		    break;
		case '[':
		    if (type)
			safe_str("%[", new, &bp);
		    else
			safe_chr('[', new, &bp);
		    break;
		case ']':
		    if (type)
			safe_str("%]", new, &bp);
		    else
			safe_chr(']', new, &bp);
		    break;
		case '{':
		    if (type)
			safe_str("%{", new, &bp);
		    else
			safe_chr('{', new, &bp);
		    break;
		case '}':
		    if (type)
			safe_str("%}", new, &bp);
		    else
			safe_chr('}', new, &bp);
		    break;
		case '(':
		    if (type)
			safe_str("%(", new, &bp);
		    else
			safe_chr('(', new, &bp);
		    break;
		case ')':
		    if (type)
			safe_str("%)", new, &bp);
		    else
			safe_chr(')', new, &bp);
		    break;
		case '\r':
		    break;
		case '\n':
		    if (type)
			safe_str("%r", new, &bp);
		    else
			safe_chr(' ', new, &bp);
		    break;
		default:
		    safe_chr(*j, new, &bp);
		}
	}
	*bp = '\0';
	return new;
}

/*
 * capitalizes an entire string
 */

char *upcasestr(s)
char *s;
{
	char *p;

	for (p = s; p && *p; p++)
		*p = ToUpper(*p);
	return s;
}

/*
 * returns a pointer to the next character in s matching c, or a pointer to
 * the \0 at the end of s.  Yes, this is a lot like index, but not exactly.
 */
char *seek_char(s, c)
const char *s;
char c;
{
	char *cp;

	cp = (char *)s;
	while (cp && *cp && (*cp != c))
		cp++;
	return (cp);
}

/*
 * ---------------------------------------------------------------------------
 * * munge_space: Compress multiple spaces to one space, also remove leading and
 * * trailing spaces.
 */

char *munge_space(string)
char *string;
{
	char *buffer, *p, *q;

	buffer = alloc_lbuf("munge_space");
	p = string;
	q = buffer;
	while (p && *p && isspace(*p))
		p++;		/*
				 * remove inital spaces 
				 */
	while (p && *p) {
		while (*p && !isspace(*p))
			*q++ = *p++;
		while (*p && isspace(*++p)) ;
		if (*p)
			*q++ = ' ';
	}
	*q = '\0';		/*
				 * remove terminal spaces and terminate
				 * string 
				 */
	return (buffer);
}

/*
 * ---------------------------------------------------------------------------
 * * trim_spaces: Remove leading and trailing spaces.
 */

char *trim_spaces(string)
char *string;
{
	char *buffer, *p, *q;

	buffer = alloc_lbuf("trim_spaces");
	p = string;
	q = buffer;
	while (p && *p && isspace(*p))	/* remove inital spaces */
		p++;
	while (p && *p) {
		while (*p && !isspace(*p))	/* copy nonspace chars */
			*q++ = *p++;
		while (*p && isspace(*p))	/* compress spaces */
			p++;
		if (*p)
			*q++ = ' ';	/* leave one space */ 
	}
	*q = '\0';		/* terminate string */
	return (buffer);
}

/*
 * ---------------------------------------------------------------------------
 * grabto: Return portion of a string up to the indicated character.  Also
 * returns a modified pointer to the string ready for another call.
 */

char *grabto(str, targ)
char **str, targ;
{
	char *savec, *cp;

	if (!str || !*str || !**str)
		return NULL;

	savec = cp = *str;
	while (*cp && *cp != targ)
		cp++;
	if (*cp)
		*cp++ = '\0';
	*str = cp;
	return savec;
}

int string_compare(s1, s2)
const char *s1, *s2;
{
#ifndef STANDALONE
	if (!mudconf.space_compress) {
		while (*s1 && *s2 && ToLower(*s1) == ToLower(*s2))
			s1++, s2++;

		return (ToLower(*s1) - ToLower(*s2));
	} else {
#endif
		while (isspace(*s1))
			s1++;
		while (isspace(*s2))
			s2++;
		while (*s1 && *s2 && ((ToLower(*s1) == ToLower(*s2)) ||
				      (isspace(*s1) && isspace(*s2)))) {
			if (isspace(*s1) && isspace(*s2)) {	/* skip all 
								 * other spaces 
								 */
				while (isspace(*s1))
					s1++;
				while (isspace(*s2))
					s2++;
			} else {
				s1++;
				s2++;
			}
		}
		if ((*s1) && (*s2))
			return (1);
		if (isspace(*s1)) {
			while (isspace(*s1))
				s1++;
			return (*s1);
		}
		if (isspace(*s2)) {
			while (isspace(*s2))
				s2++;
			return (*s2);
		}
		if ((*s1) || (*s2))
			return (1);
		return (0);
#ifndef STANDALONE
	}
#endif
}

int string_prefix(string, prefix)
const char *string, *prefix;
{
	int count = 0;

	while (*string && *prefix && ToLower(*string) == ToLower(*prefix))
		string++, prefix++, count++;
	if (*prefix == '\0')	/* Matched all of prefix */
		return (count);
	else
		return (0);
}

/*
 * accepts only nonempty matches starting at the beginning of a word 
 */

const char *string_match(src, sub)
const char *src, *sub;
{
	if ((*sub != '\0') && (src)) { 
		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;
}

/*
 * ---------------------------------------------------------------------------
 * replace_string: Returns an lbuf containing string STRING with all occurances
 * of OLD replaced by NEW. OLD and NEW may be different lengths.
 * (mitch 1 feb 91)
 * replace_string_ansi: Like replace_string, but sensitive about ANSI codes.
 */

char *replace_string(old, new, string)
const char *old, *new, *string;
{
	char *result, *r, *s;
	int olen;

	if (string == NULL)
		return NULL;
	s = (char *)string;
	olen = strlen(old);
	r = result = alloc_lbuf("replace_string");
	while (*s) {

		/* Copy up to the next occurrence of the first char of OLD */

		while (*s && *s != *old) {
			safe_chr(*s, result, &r);
			s++;
		}

		/*
		 * If we are really at an OLD, append NEW to the result and
		 * bump the input string past the occurrence of
		 * OLD. Otherwise, copy the char and try again. 
		 */

		if (*s) {
			if (!strncmp(old, s, olen)) {
				safe_str((char *)new, result, &r);
				s += olen;
			} else {
				safe_chr(*s, result, &r);
				s++;
			}
		}
	}
	*r = '\0';
	return result;
}

char *replace_string_ansi(old, new, string)
    const char *old, *new, *string;
{
    char *result, *r, *s, *t, *savep;
    int olen, have_normal, new_ansi;

    if (!string)
	return NULL;

    s = (char *) string;
    r = result = alloc_lbuf("replace_string_ansi");

    olen = strlen(old);

    /* Scan the contents of the string. Figure out whether we have any
     * embedded ANSI codes.
     */
    new_ansi = (((char *) index(new, ESC_CHAR)) == NULL) ? 0 : 1;

    have_normal = 1;
    while (*s) {

	/* Copy up to the next occurrence of the first char of OLD. */

	while (*s && (*s != *old)) {
	    if (*s == ESC_CHAR) {
		/* Start of an ANSI code. Skip to the end. */
		savep = s;
		while (*s && (*s != ANSI_END)) {
		    safe_chr(*s, result, &r);
		    s++;
		}
		if (*s) {
		    safe_chr(*s, result, &r);
		    s++;
		}
		if (!strncmp(savep, ANSI_NORMAL, 4))
		    have_normal = 1;
		else
		    have_normal = 0;
	    } else {
		safe_chr(*s, result, &r);
		s++;
	    }
	}

	/* If we are really at an OLD, append NEW to the result and
	 * bump the input string past the occurrence of OLD. Otherwise,
	 * copy the char and try again.
	 */

	if (*s) {
	    if (!strncmp(old, s, olen)) {

		/* If the string contains no ANSI characters, we can
		 * just copy it. Otherwise we need to scan through it.
		 */

		if (!new_ansi) {
		    safe_str((char *) new, result, &r);
		} else {
		    t = (char *) new;
		    while (*t) {
			if (*t == ESC_CHAR) {
			    /* Start of an ANSI code. Skip to the end. */
			    savep = t;
			    while (*t && (*t != ANSI_END)) {
				safe_chr(*t, result, &r);
				t++;
			    }
			    if (*t) {
				safe_chr(*t, result, &r);
				t++;
			    }
			    if (!strncmp(savep, ANSI_NORMAL, 4))
				have_normal = 1;
			    else
				have_normal = 0;
			} else {
			    safe_chr(*t, result, &r);
			    t++;
			}
		    }
		}
		s += olen;
	    } else {
		/* We have to handle the case where the first character
		 * in OLD is the ANSI escape character. In that case
		 * we move over and copy the entire ANSI code. Otherwise
		 * we just copy the character.
		 */
		if (*old == ESC_CHAR) {
		    savep = s;
		    while (*s && (*s != ANSI_END)) {
			safe_chr(*s, result, &r);
			s++;
		    }
		    if (*s) {
			safe_chr(*s, result, &r);
			s++;
		    }
		    if (!strncmp(savep, ANSI_NORMAL, 4))
			have_normal = 1;
		    else
			have_normal = 0;
		} else {
		    safe_chr(*s, result, &r);
		    s++;
		}
	    }
	}
    }

    if (!have_normal)
	safe_ansi_normal(result, &r);

    *r = '\0';
    return result;
}

int minmatch(str, target, min)
char *str, *target;
int min;
{
	while (*str && *target && (ToLower(*str) == ToLower(*target))) {
		str++;
		target++;
		min--;
	}
	if (*str)
		return 0;
	if (!*target)
		return 1;
	return ((min <= 0) ? 1 : 0);
}

/* ---------------------------------------------------------------------------
 * safe_copy_str, safe_copy_long_str, safe_chr_real_fn - Copy buffers, 
 * watching for overflows.
 */

INLINE int 
safe_copy_str(src, buff, bufp, max)
    char *src, *buff, **bufp;
    int max;
{
    char *tp, *maxtp, *longtp;
    int n, len;

    tp = *bufp;
    if (src == NULL) {
	*tp = '\0';
	return 0;
    }
    maxtp = buff + max;
    longtp = tp + 7;
    maxtp = (maxtp < longtp) ? maxtp : longtp;

    while (*src && (tp < maxtp))
	*tp++ = *src++;

    if (*src == '\0') {
	*bufp = tp;
	if ((tp - buff) < max)
	    *tp = '\0';
	else
	    buff[max] = '\0';
	return 0;
    }

    len = strlen(src);
    n = max - (tp - buff);
    if (n <= 0) {
	*tp = '\0';
	return (len);
    }
    n = ((len < n) ? len : n);
    bcopy(src, tp, n);
    tp += n;
    *tp = '\0';
    *bufp = tp;

    return (len - n);
}

int 
safe_copy_long_str(src, buff, bufp, max)
    char *src, *buff, **bufp;
    int max;
{
    int len, n;
    char *tp;

    tp = *bufp;
    if (src == NULL) {
	*tp = '\0';
	return 0;
    }

    len = strlen(src);
    n = max - (tp - buff);
    if (n < 0)
	 n = 0;

    strncpy (tp, src, n);
    buff[max] = '\0';

    if (len <= n) {
	*bufp = tp + len;
	return (0);
    } else {
	*bufp = tp + n;
	return (len-n);
    }
}


INLINE void safe_copy_known_str(src, known, buff, bufp, max)
    char *src, *buff, **bufp;
    int known, max;
{
    int n;
    char *tp, *maxtp;

    tp = *bufp;

    if (!src) {
	*tp = '\0';
	return;
    }

    if (known > 7) {
	n = max - (tp - buff);
	if (n <= 0) {
	    *tp = '\0';
	    return;
	}
	n = ((known < n) ? known : n);
	bcopy(src, tp, n);
	tp += n;
	*tp = '\0';
	*bufp = tp;
	return;
    }

    maxtp = buff + max;
    while (*src && (tp < maxtp))
	*(tp)++ = *src++;

    if (!*src) {
	if (tp < maxtp)
	    *tp = '\0';
	else
	    *maxtp = '\0';
    }

    *bufp = tp;
}


INLINE int 
safe_chr_real_fn(src, buff, bufp, max)
    char src, *buff, **bufp;
    int max;
{
    char *tp;
    int retval = 0;

    tp = *bufp;
    if ((tp - buff) < max) {
	*tp++ = src;
	*bufp = tp;
	*tp = '\0';
    } else {
	buff[max] = '\0';
	retval = 1;
    }

    return retval;
}

/* ---------------------------------------------------------------------------
 * More utilities.
 */

int matches_exit_from_list(str, pattern)
char *str, *pattern;
{
	char *s;

	while (*pattern) {
		for (s = str;	/* check out this one */
		     (*s && (ToLower(*s) == ToLower(*pattern)) &&
		      *pattern && (*pattern != EXIT_DELIMITER));
		     s++, pattern++) ;

		/* Did we match it all? */

		if (*s == '\0') {

			/* Make sure nothing afterwards */

			while (*pattern && isspace(*pattern))
				pattern++;

			/* Did we get it? */

			if (!*pattern || (*pattern == EXIT_DELIMITER))
				return 1;
		}
		/* We didn't get it, find next string to test */

		while (*pattern && *pattern++ != EXIT_DELIMITER) ;
		while (isspace(*pattern))
			pattern++;
	}
	return 0;
}

int ltos(s, num)
char *s;
long num;
{
	/* Mark Vasoll's long int to string converter. */
	char buf[20], *p;
	long anum;

	p = buf;

	/* absolute value */
	anum = (num < 0) ? -num : num;

	/* build up the digits backwards by successive division */
	while (anum > 9) {
		*p++ = '0' + (anum % 10);
		anum /= 10;
	}

	/* put in the sign if needed */
	if (num < 0)
		*s++ = '-';

	/* put in the last digit, this makes very fast single digits numbers */
	*s++ = '0' + anum;

	/* reverse the rest of the digits (if any) into the provided buf */
	while (p-- > buf)
		*s++ = *p;

	/* terminate the resulting string */
	*s = '\0';
	return 0;
}

INLINE void safe_ltos(s, bufc, num)
char *s;
char **bufc;
long num;
{
	/* Mark Vasoll's long int to string converter. */
	char buf[20], *p, *tp, *endp;
	long anum;

	p = buf;
	tp = *bufc;

	/* absolute value */
	anum = (num < 0) ? -num : num;

	/* build up the digits backwards by successive division */
	while (anum > 9) {
		*p++ = '0' + (anum % 10);
		anum /= 10;
	}

	if (tp > s + LBUF_SIZE - 21) {
		endp = s + LBUF_SIZE - 1;
		/* put in the sign if needed */
		if (num < 0 && (tp < endp))
			*tp++ = '-';

		/* put in the last digit, this makes very fast single 
		 * digits numbers
		 */
		if (tp < endp)
			*tp++ = '0' + anum;

		/* reverse the rest of the digits (if any) into the
		 * provided buf
		 */
		while ((p-- > buf) && (tp < endp))
			*tp++ = *p;
	} else {
		if (num < 0)
			*tp++ = '-';
		*tp++ = '0' + anum;
		while (p-- > buf)
			*tp++ = *p;
	}

	/* terminate the resulting string */
	*tp = '\0';
	*bufc = tp;
}