/* stringutil.c -- string utilities */

#include "autoconf.h"
#include "copyright.h"
#ifndef	lint
static char *RCSid = "$Id: stringutil.c,v 1.9 1995/03/29 17:26:48 ambar Exp $";
USE(RCSid);
#endif

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

/*
 * returns a pointer to the non-space character in s, or a NULL if s == NULL
 * or *s == NULL or s has only spaces.
 */
char *
skip_space(s)
    const char *s;
{
    char *cp;

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

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

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

/*
 * Returns string STRING with all occurances * of OLD replaced by NEW. OLD
 * and NEW may be different lengths. Modifies string, so: Note - STRING must
 * already be allocated large enough to handle the new size. (mitch 1 feb 91)
 */

char *
replace_string_inplace(old, new, string)
    const char *old, *new;
    char *string;
{
    char *s;

    s = replace_string(old, new, string);
    strcpy(string, s);
    free_lbuf(s);
    return string;
}

/* Counts occurances of C in STR. - mnp 7 feb 91 */

int 
count_chars(str, c)
    const char *str, c;
{
    register out = 0;
    register const char *p = str;

    if (p)
	while (*p != '\0')
	    if (*p++ == c)
		out++;
    return out;
}

/*
 * Returns an allocated, null-terminated array of strings, broken on SEP. The
 * array returned points into the original, >> modified << string. - mnp 7
 * feb 91
 */

char **
string2list(str, sep)
    char *str;
    const char sep;
{
    int count = 0;
    char **out = NULL;
    char *end, *beg = str;

    if (str) {
	if (!(out = (char **) XMALLOC(sizeof(char *) * strlen(str), "string2list"))) {
	    log_perror("ALC", "FAIL", NULL, "NO MEM in string2list()");
	    return NULL;
	}
	for (;;) {
	    while (*beg == sep)
		beg++;
	    if (*beg == '\0')
		break;
	    out[count++] = beg;
	    for (end = beg; *end != '\0' && *end != sep; end++);
	    if (*end == '\0')
		break;
	    *end++ = '\0';
	    beg = end;
	}
	out[count] = NULL;
    }
    if (out)
	out = (char **) realloc((char *) out, sizeof(char *) * count + 1);

    return out;
}

/* returns the number of identical characters in the two strings */
int 
prefix_match(s1, s2)
    const char *s1, *s2;
{
    int count = 0;

    while (*s1 && *s2 && (ToLower(*s1) == ToLower(*s2)))
	s1++, s2++, count++;
    /* If the whole string matched, count the null.  (Yes really.) */
    if (!*s1 && !*s2)
	count++;
    return count;
}

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

INLINE
char *
strsave(s)
    const char *s;
{
    char *p;
    p = (char *) XMALLOC(sizeof(char) * (strlen(s) + 1), "strsave");

    if (p)
	strcpy(p, s);
    return p;
}

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

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

#ifdef PUEBLO_SUPPORT
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;
}
#endif /* PUEBLO_SUPPORT */

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