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