/* stringutil.c */

#include "copyright.h"

/* String utilities */

#include <ctype.h>
#include <stdio.h>
#ifndef VMS
#include <strings.h>
#else
#include <string.h>
#endif

#include <sys/types.h>

#include "mudconf.h"
#include "config.h"
#include "externs.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(const char *s)
{
  char *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 strchr, but not exactly.
 */
char *seek_char(const char *s, char c)
{
  char *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(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(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 (char **str, char 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(const char *s1, const char *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(const char *string, const char *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(const char *src, const char *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(const char *old, const char *new, const char *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(const char *old, const char *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(const char *str, const char 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(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"))) {
      perror("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(const char *s1, const char *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 (char *str, char *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);
}

char *strsave(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 - Copy buffers, watching for overflows.
 */

int safe_copy_str(char *src, char *buff, char **bufp, int max)
{
char	*tp;

	tp = *bufp;
	if (src == NULL) return 0;
	while (*src && ((tp - buff) < max))
		*tp++ = *src++;
	*bufp = tp;
	return strlen(src);
}

int safe_copy_chr(char src, char *buff, char **bufp, int max)
{
char	*tp;
int	retval;

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

int matches_exit_from_list (char *str, char *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;
}