tinymush-3.1p1/game/backups/
tinymush-3.1p1/game/bin/
tinymush-3.1p1/game/data/
tinymush-3.1p1/game/modules/
tinymush-3.1p1/game/modules/old/
tinymush-3.1p1/src/modules/comsys/
tinymush-3.1p1/src/modules/hello/
tinymush-3.1p1/src/modules/mail/
tinymush-3.1p1/src/tools/
/* stringutil.c - string utilities */
/* $Id: stringutil.c,v 1.53 2003/02/24 18:05:23 rmg Exp $ */

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

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by code */

#include "ansi.h"	/* required by code */

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

/* ---------------------------------------------------------------------------
 * ANSI character-to-number translation table.
 */

int ansi_nchartab[256] =
{
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,               0,               I_ANSI_BBLUE,    I_ANSI_BCYAN,
    0,               0,               0,               I_ANSI_BGREEN,
    0,               0,               0,               0,
    0,               I_ANSI_BMAGENTA, 0,               0,
    0,               0,               I_ANSI_BRED,     0,
    0,               0,               0,               I_ANSI_BWHITE,
    I_ANSI_BBLACK,   I_ANSI_BYELLOW,  0,               0,
    0,               0,               0,               0,
    0,               0,               I_ANSI_BLUE,     I_ANSI_CYAN,
    0,               0,               I_ANSI_BLINK,    I_ANSI_GREEN,
    I_ANSI_HILITE,   I_ANSI_INVERSE,  0,               0,
    0,               I_ANSI_MAGENTA,  I_ANSI_NORMAL,   0,
    0,               0,               I_ANSI_RED,      0,
    0,               I_ANSI_UNDER,    0,               I_ANSI_WHITE,
    I_ANSI_BLACK,    I_ANSI_YELLOW,   0,               0,
    0,               0,               0,               0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
};

/* ---------------------------------------------------------------------------
 * ANSI number-to-character translation table.
 */

char ansi_lettab[I_ANSI_NUM] =
{
	'\0',	'h',	'\0',	'\0',	'u',	'f',	'\0',	'i',
	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',
	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',
	'\0',	'\0',	'\0',	'\0',	'\0',	'\0',	'x',	'r',
	'g',	'y',	'b',	'm',	'c',	'w',	'\0',	'\0',
	'X',	'R',	'G',	'Y',	'B',	'M',	'C',	'W'
};

/* ---------------------------------------------------------------------------
 * ANSI packed state definitions -- number-to-bitmask translation table.
 *
 * The mask specifies the state bits that are altered by a particular ansi
 * code. Bits are laid out as follows:
 *
 *  0x1000 -- No ansi. Every valid ansi code clears this bit.
 *  0x0800 -- inverse
 *  0x0400 -- flash
 *  0x0200 -- underline
 *  0x0100 -- highlight
 *  0x0080 -- "use default bg", set by ansi normal, cleared by other bg's
 *  0x0070 -- three bits of bg color
 *  0x0008 -- "use default fg", set by ansi normal, cleared by other fg's
 *  0x0007 -- three bits of fg color
 */

int ansi_mask_bits[I_ANSI_LIM] = {
	0x1fff, 0x1100, 0x1100, 0,      0x1200, 0x1400, 0,      0x1800, 0, 0,
	0,      0,      0,      0,      0,      0,      0,      0,      0, 0,
	0,      0x1100, 0x1100, 0,      0x1200, 0x1400, 0,      0x1800, 0, 0,
	0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0, 0,
	0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0, 0
};

/* ---------------------------------------------------------------------------
 * ANSI packed state definitions -- number-to-bitvalue translation table.
 */

int ansi_bits[I_ANSI_LIM] = {
	0x0099, 0x0100, 0x0000, 0,      0x0200, 0x0400, 0,      0x0800, 0, 0,
	0,      0,      0,      0,      0,      0,      0,      0,      0, 0,
	0,      0x0000, 0x0000, 0,      0x0000, 0x0000, 0,      0x0000, 0, 0,
	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0, 0,
	0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0, 0
};

/* ---------------------------------------------------------------------------
 * strip_ansi -- return a new string with escape codes removed
 */

char *strip_ansi(s)
const char *s;
{
	static char buf[LBUF_SIZE];
	char *p = buf;

	if (s) {
	  while (*s == ESC_CHAR) {
	    skip_esccode(s);
	  }

	  while (*s) {
	    *p++ = *s++;
	    while (*s == ESC_CHAR) {
	      skip_esccode(s);
	    }
	  }
	}

	*p = '\0';
	return buf;
}

/* ---------------------------------------------------------------------------
 * strip_ansi_len -- count non-escape-code characters
 */

int strip_ansi_len(s)
const char *s;
{
	int n = 0;

	if (s) {
		while (*s == ESC_CHAR) {
			skip_esccode(s);
		}

		while (*s) {
			++s, ++n;
			while (*s == ESC_CHAR) {
				skip_esccode(s);
			}
		}
	}

	return n;
}

/* normal_to_white -- This function implements the NOBLEED flag */

char *normal_to_white(raw)
const char *raw;
{
	static char buf[LBUF_SIZE];
	char *p = (char *)raw;
	char *q = buf;
	char *just_after_csi;
	char *just_after_esccode = p;
	unsigned int param_val;
	int has_zero;

	while (p && *p) {
		if (*p == ESC_CHAR) {
			safe_known_str(just_after_esccode, p - just_after_esccode, buf, &q);
			if (p[1] == ANSI_CSI) {
				safe_chr(*p, buf, &q);
				++p;
				safe_chr(*p, buf, &q);
				++p;
				just_after_csi = p;
				has_zero = 0;
				while ((*p & 0xf0) == 0x30) {
					if (*p == '0')
						has_zero = 1;
					++p;
				}
				while ((*p & 0xf0) == 0x20) {
					++p;
				}
				if (*p == ANSI_END && has_zero) {
					/* it really was an ansi code,
					 * go back and fix up the zero
					 */
					p = just_after_csi;
					param_val = 0;
					while ((*p & 0xf0) == 0x30) {
						if (*p < 0x3a) {
							param_val <<= 1;
							param_val += (param_val << 2) + (*p & 0x0f);
							safe_chr(*p, buf, &q);
						} else {
							if (param_val == 0) {
								/* ansi normal */
								safe_known_str("m\033[37m\033[", 8, buf, &q);
							} else {
								/* some other color */
								safe_chr(*p, buf, &q);
							}
							param_val = 0;
						}
						++p;
					}
					while ((*p & 0xf0) == 0x20) {
						++p;
					}
					safe_chr(*p, buf, &q);
					++p;
					if (param_val == 0) {
						safe_known_str(ANSI_WHITE, 5, buf, &q);
					}
				} else {
					++p;
					safe_known_str(just_after_csi, p - just_after_csi, buf, &q);
				}
			} else {
				safe_copy_esccode(p, buf, &q);
			}
			just_after_esccode = p;
		} else {
			++p;
		}
	}
	safe_known_str(just_after_esccode, p - just_after_esccode, buf, &q);
	return buf;
}

char *ansi_transition_esccode(ansi_before, ansi_after)
int ansi_before, ansi_after;
{
	int ansi_bits_set, ansi_bits_clr;
	char *p;
	static char buffer[64];

	if (ansi_before == ansi_after)
		return "";

	buffer[0] = ESC_CHAR;
	buffer[1] = ANSI_CSI;
	p = buffer + 2;

	/* If they turn off any highlight bits, or they change from some color
	 * to default color, we need to use ansi normal first.
	 */

	ansi_bits_set = (~ansi_before) & ansi_after;
	ansi_bits_clr = ansi_before & (~ansi_after);
	if ((ansi_bits_clr & 0xf00) ||		/* highlights off */
	    (ansi_bits_set & 0x088) ||		/* normal to color */
	    (ansi_bits_clr == 0x1000)) {	/* explicit normal */
		strcpy(p, "0;"); p += 2;
		ansi_bits_set = (~ansi_bits[0]) & ansi_after;
		ansi_bits_clr = ansi_bits[0] & (~ansi_after);
	}

	/* Next reproduce the highlight state */

	if (ansi_bits_set & 0x100) {
		strcpy(p, "1;"); p += 2;
	}
	if (ansi_bits_set & 0x200) {
		strcpy(p, "4;"); p += 2;
	}
	if (ansi_bits_set & 0x400) {
		strcpy(p, "5;"); p += 2;
	}
	if (ansi_bits_set & 0x800) {
		strcpy(p, "7;"); p += 2;
	}

	/* Foreground color */
	if ((ansi_bits_set | ansi_bits_clr) & 0x00f) {
		strcpy(p, "30;"); p += 3;
		p[-2] |= (ansi_after & 0x00f);
	}

	/* Background color */
	if ((ansi_bits_set | ansi_bits_clr) & 0x0f0) {
		strcpy(p, "40;"); p += 3;
		p[-2] |= ((ansi_after & 0x0f0) >> 4);
	}

	/* Terminate */
	if (p > buffer + 2) {
		p[-1] = ANSI_END;
		/* Buffer is already null-terminated by strcpy */
	} else {
		buffer[0] = '\0';
	}
	return buffer;
}

char *ansi_transition_mushcode(ansi_before, ansi_after)
int ansi_before, ansi_after;
{
	int ansi_bits_set, ansi_bits_clr;
	char *p;
	static char ansi_mushcode_fg[9] = "xrgybmcw";
	static char ansi_mushcode_bg[9] = "XRGYBMCW";
	static char buffer[64];

	if (ansi_before == ansi_after)
		return "";

	p = buffer;

	/* If they turn off any highlight bits, or they change from some color
	 * to default color, we need to use ansi normal first.
	 */

	ansi_bits_set = (~ansi_before) & ansi_after;
	ansi_bits_clr = ansi_before & (~ansi_after);
	if ((ansi_bits_clr & 0xf00) ||		/* highlights off */
	    (ansi_bits_set & 0x088) ||		/* normal to color */
	    (ansi_bits_clr == 0x1000)) {	/* explicit normal */
		strcpy(p, "%xn"); p += 3;
		ansi_bits_set = (~ansi_bits[0]) & ansi_after;
		ansi_bits_clr = ansi_bits[0] & (~ansi_after);
	}

	/* Next reproduce the highlight state */

	if (ansi_bits_set & 0x100) {
		strcpy(p, "%xh"); p += 3;
	}
	if (ansi_bits_set & 0x200) {
		strcpy(p, "%xu"); p += 3;
	}
	if (ansi_bits_set & 0x400) {
		strcpy(p, "%xf"); p += 3;
	}
	if (ansi_bits_set & 0x800) {
		strcpy(p, "%xi"); p += 3;
	}

	/* Foreground color */
	if ((ansi_bits_set | ansi_bits_clr) & 0x00f) {
		strcpy(p, "%xx"); p += 3;
		p[-1] = ansi_mushcode_fg[(ansi_after & 0x00f)];
	}

	/* Background color */
	if ((ansi_bits_set | ansi_bits_clr) & 0x0f0) {
		strcpy(p, "%xX"); p += 3;
		p[-1] = ansi_mushcode_bg[(ansi_after & 0x0f0) >> 4];
	}

	/* Terminate */
	*p = '\0';
	return buffer;
}

char *ansi_transition_letters(ansi_before, ansi_after)
int ansi_before, ansi_after;
{
	int ansi_bits_set, ansi_bits_clr;
	char *p;
	static char ansi_mushcode_fg[9] = "xrgybmcw";
	static char ansi_mushcode_bg[9] = "XRGYBMCW";
	static char buffer[64];

	if (ansi_before == ansi_after)
		return "";

	p = buffer;

	/* If they turn off any highlight bits, or they change from some color
	 * to default color, we need to use ansi normal first.
	 */

	ansi_bits_set = (~ansi_before) & ansi_after;
	ansi_bits_clr = ansi_before & (~ansi_after);
	if ((ansi_bits_clr & 0xf00) ||		/* highlights off */
	    (ansi_bits_set & 0x088) ||		/* normal to color */
	    (ansi_bits_clr == 0x1000)) {	/* explicit normal */
		*p++ = 'n';
		ansi_bits_set = (~ansi_bits[0]) & ansi_after;
		ansi_bits_clr = ansi_bits[0] & (~ansi_after);
	}

	/* Next reproduce the highlight state */

	if (ansi_bits_set & 0x100) {
		*p++ = 'h';
	}
	if (ansi_bits_set & 0x200) {
		*p++ = 'u';
	}
	if (ansi_bits_set & 0x400) {
		*p++ = 'f';
	}
	if (ansi_bits_set & 0x800) {
		*p++ = 'i';
	}

	/* Foreground color */
	if ((ansi_bits_set | ansi_bits_clr) & 0x00f) {
		*p++ = ansi_mushcode_fg[(ansi_after & 0x00f)];
	}

	/* Background color */
	if ((ansi_bits_set | ansi_bits_clr) & 0x0f0) {
		*p++ = ansi_mushcode_bg[(ansi_after & 0x0f0) >> 4];
	}

	/* Terminate */
	*p = '\0';
	return buffer;
}

/* ansi_map_states -- Identify ansi state of every character in a string */

int ansi_map_states(s, m)
const char *s;
int **m;
{
	static int ansi_map[LBUF_SIZE + 1];
	int n, ansi_state;

	n = 0;
	ansi_state = ANST_NORMAL;

	while (*s) {
		if (*s == ESC_CHAR) {
			track_esccode(s, ansi_state);
		} else {
			ansi_map[n++] = (((int)*s++) << 16) | ansi_state;
		}
	}

	ansi_map[n] = ANST_NORMAL;

	*m = ansi_map;
	return n;
}

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

char *translate_string(str, type)
char *str;
int type;
{
	static char new[LBUF_SIZE];
	char *bp;

	bp = new;

	if (type) {
		int ansi_state = ANST_NORMAL;
		int ansi_state_prev = ANST_NORMAL;

		while (*str) {
			switch (*str) {
			case ESC_CHAR:
				while (*str == ESC_CHAR) {
					track_esccode(str, ansi_state);
				}
				safe_str(ansi_transition_mushcode(ansi_state_prev, ansi_state), new, &bp);
				ansi_state_prev = ansi_state;
				continue;
			case ' ':
				if (str[1] == ' ') {
					safe_known_str("%b", 2, new, &bp);
				} else {
					safe_chr(' ', new, &bp);
				}
				break;
			case '\\': case '%': case '[': case ']':
			case '{':  case '}': case '(': case ')':
				safe_chr('%', new, &bp);
				safe_chr(*str, new, &bp);
				break;
			case '\r':
				break;
			case '\n':
				safe_known_str("%r", 2, new, &bp);
				break;
			case '\t':
				safe_known_str("%t", 2, new, &bp);
				break;
			default:
				safe_chr(*str, new, &bp);
			}
			str++;
		}
	} else {
		while (*str) {
			switch (*str) {
			case ESC_CHAR:
				skip_esccode(str);
				continue;
			case '\r':
				break;
			case '\n': case '\t':
				safe_chr(' ', new, &bp);
				break;
			default:
				safe_chr(*str, new, &bp);
			}
			str++;
		}
	}
	*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;
}

/*
 * ---------------------------------------------------------------------------
 * 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 initial 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;
{
	if (mudstate.standalone || mudconf.space_compress) {
		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);
	} else {
		while (*s1 && *s2 && tolower(*s1) == tolower(*s2))
			s1++, s2++;

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

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)
 *
 * edit_string: Like replace_string, but sensitive about ANSI codes, and
 * handles special ^ and $ cases.
 */

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;
}

void edit_string(src, dst, from, to)
char *src, **dst, *from, *to;
{
	char *cp, *p;
	int ansi_state, to_ansi_set, to_ansi_clr, tlen, flen;

        /* We may have gotten an ANSI_NORMAL termination to OLD and NEW,
	 * that the user probably didn't intend to be there. (If the
	 * user really did want it there, he simply has to put a double
	 * ANSI_NORMAL in; this is non-intuitive but without it we can't
	 * let users swap one ANSI code for another using this.)  Thus,
	 * we chop off the terminating ANSI_NORMAL on both, if there is
	 * one.
	 */

	p = from + strlen(from) - 4;
	if (p >= from && !strcmp(p, ANSI_NORMAL))
	    *p = '\0';

	p = to + strlen(to) - 4;
	if (p >= to && !strcmp(p, ANSI_NORMAL))
	    *p = '\0';


	/* Scan the contents of the TO string. Figure out whether we
	 * have any embedded ANSI codes.
	 */
	ansi_state = ANST_NONE;
	track_all_esccodes(to, p, ansi_state);
	to_ansi_set = (~ANST_NONE) & ansi_state;
	to_ansi_clr = ANST_NONE & (~ansi_state);
	tlen = p - to;

	/*
	 * Do the substitution.  Idea for prefix/suffix from R'nice@TinyTIM 
	 */

	cp = *dst = alloc_lbuf("edit_string");

	if (!strcmp(from, "^")) {
		/*
		 * Prepend 'to' to string 
		 */

		safe_known_str(to, tlen, *dst, &cp);
		safe_copy_tracking(src, p, ansi_state, *dst, &cp);

	} else if (!strcmp(from, "$")) {

		/*
		 * Append 'to' to string 
		 */

		ansi_state = ANST_NONE;
		safe_copy_tracking(src, p, ansi_state, *dst, &cp);

		ansi_state |= to_ansi_set;
		ansi_state &= ~to_ansi_clr;
		safe_known_str(to, tlen, *dst, &cp);

	} else {
		/*
		 * Replace all occurances of 'from' with 'to'.  Handle the
		 * special cases of from = \$ and \^. 
		 */

		if (((from[0] == '\\') || (from[0] == '%')) &&
		    ((from[1] == '$') || (from[1] == '^')) &&
		    (from[2] == '\0'))
			from++;

		flen = strlen(from);

		ansi_state = ANST_NONE;
		while (*src) {

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

			p = src;
			while (*src && (*src != *from)) {
				if (*src == ESC_CHAR) {
					track_esccode(src, ansi_state);
				} else {
					++src;
				}
			}
			safe_known_str(p, src - p, *dst, &cp);

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

			if (*src) {
				if (!strncmp(from, src, flen)) {
					/* Apply whatever ANSI transition
					 * happens in TO */
					ansi_state |= to_ansi_set;
					ansi_state &= ~to_ansi_clr;

					safe_known_str(to, tlen, *dst, &cp);
					src += flen;
				} else {
					/* We have to handle the case where
					 * the first character in FROM is the
					 * ANSI escape character. In that case
					 * we move over and copy the entire
					 * ANSI code. Otherwise we just copy
					 * the character.
					 */
					if (*from == ESC_CHAR) {
						p = src;
						track_esccode(src, ansi_state);
						safe_known_str(p, src - p,
							       *dst, &cp);
					} else {
						safe_chr(*src, *dst, &cp);
						++src;
					}
				}
			}
		}
	}

        safe_str(ansi_transition_esccode(ansi_state, ANST_NONE), *dst, &cp);
}

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 void
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;
    }
    maxtp = buff + max;
    longtp = tp + 7;
    maxtp = (maxtp < longtp) ? maxtp : longtp;

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

    if (*src == '\0') { /* copied whole src, and tp is at most maxtp */
	*tp = '\0';
	*bufp = tp;
	return;
    }

    len = strlen(src);
    n = max - (tp - buff); /* tp is either maxtp or longtp */
    if (n <= 0) {
	*tp = '\0';
	*bufp = tp;
	return;
    }
    n = ((len < n) ? len : n);
    memcpy(tp, src, n);
    tp += n;
    *tp = '\0';
    *bufp = tp;
}


INLINE int 
safe_copy_str_fn(src, buff, bufp, max)
    const char *src;
    char *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') { /* copied whole src, and tp is at most maxtp */
	*tp = '\0';
	*bufp = tp;
	return 0;
    }

    len = strlen(src);
    n = max - (tp - buff); /* tp is either maxtp or longtp */
    if (n <= 0) {
    	len -= (tp - *bufp);
	*tp = '\0';
	*bufp = tp;
	return (len);
    }
    n = ((len < n) ? len : n);
    memcpy(tp, src, 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_known_str(src, known, buff, bufp)
    const char *src;
    char *buff, **bufp;
    int known;
{
    int n;
    char *tp, *maxtp;

    tp = *bufp;

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

    maxtp = buff + LBUF_SIZE - 1;

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

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

    *tp = '\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;
	unsigned 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' + (char)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;
	unsigned 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' + (char)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' + (char)anum;
		while (p-- > buf)
			*tp++ = *p;
	}

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