Files
nethack/src/strutil.c
2024-04-10 00:46:21 -07:00

159 lines
4.7 KiB
C

/* NetHack 3.7 strutil.c $NHDT-Date: 1709571807 2024/03/04 17:03:27 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.0 $ */
/* Copyright (c) Robert Patrick Rankin, 1991 */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h" /* for config.h+extern.h */
/* strbuf_init() initializes strbuf state for use */
void
strbuf_init(strbuf_t *strbuf)
{
strbuf->str = NULL;
strbuf->len = 0;
}
/* strbuf_append() appends given str to strbuf->str */
void
strbuf_append(strbuf_t *strbuf, const char *str)
{
int len = (int) strlen(str) + 1;
strbuf_reserve(strbuf,
len + (strbuf->str ? (int) strlen(strbuf->str) : 0));
Strcat(strbuf->str, str);
}
/* strbuf_reserve() ensure strbuf->str has storage for len characters */
void
strbuf_reserve(strbuf_t *strbuf, int len)
{
if (strbuf->str == NULL) {
strbuf->str = strbuf->buf;
strbuf->str[0] = '\0';
strbuf->len = (int) sizeof strbuf->buf;
}
if (len > strbuf->len) {
char *oldbuf = strbuf->str;
strbuf->len = len + (int) sizeof strbuf->buf;
strbuf->str = (char *) alloc(strbuf->len);
Strcpy(strbuf->str, oldbuf);
if (oldbuf != strbuf->buf)
free((genericptr_t) oldbuf);
}
}
/* strbuf_empty() frees allocated memory and set strbuf to initial state */
void
strbuf_empty(strbuf_t *strbuf)
{
if (strbuf->str != NULL && strbuf->str != strbuf->buf)
free((genericptr_t) strbuf->str);
strbuf_init(strbuf);
}
/* strbuf_nl_to_crlf() converts all occurrences of \n to \r\n */
void
strbuf_nl_to_crlf(strbuf_t *strbuf)
{
if (strbuf->str) {
int len = (int) strlen(strbuf->str);
int count = 0;
char *cp = strbuf->str;
while (*cp)
if (*cp++ == '\n')
count++;
if (count) {
strbuf_reserve(strbuf, len + count + 1);
for (cp = strbuf->str + len + count; count; --cp)
if ((*cp = cp[-count]) == '\n') {
*--cp = '\r';
--count;
}
}
}
}
/* strlen() but returns unsigned and panics if string is unreasonably long;
used by dlb as well as by nethack */
unsigned
Strlen_(
const char *str,
const char *file,
int line)
{
const char *p;
size_t len;
/* strnlen(str, LARGEST_INT) w/o requiring posix.1 headers or libraries */
for (p = str, len = 0; len < LARGEST_INT; ++len)
if (*p++ == '\0')
break;
if (len == LARGEST_INT)
panic("%s:%d string too long", file, line);
return (unsigned) len;
}
staticfn boolean pmatch_internal(const char *, const char *, boolean,
const char *);
/* guts of pmatch(), pmatchi(), and pmatchz();
match a string against a pattern */
staticfn boolean
pmatch_internal(const char *patrn, const char *strng,
boolean ci, /* True => case-insensitive,
False => case-sensitive */
const char *sk) /* set of characters to skip */
{
char s, p;
/*
* Simple pattern matcher: '*' matches 0 or more characters, '?' matches
* any single character. Returns TRUE if 'strng' matches 'patrn'.
*/
pmatch_top:
if (!sk) {
s = *strng++;
p = *patrn++; /* get next chars and pre-advance */
} else {
/* fuzzy match variant of pmatch; particular characters are ignored */
do {
s = *strng++;
} while (strchr(sk, s));
do {
p = *patrn++;
} while (strchr(sk, p));
}
if (!p) /* end of pattern */
return (boolean) (s == '\0'); /* matches iff end of string too */
else if (p == '*') /* wildcard reached */
return (boolean) ((!*patrn
|| pmatch_internal(patrn, strng - 1, ci, sk))
? TRUE
: s ? pmatch_internal(patrn - 1, strng, ci, sk)
: FALSE);
else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */
&& (p != '?' || !s)) /* & single-char wildcard */
return FALSE; /* doesn't match */
else /* return pmatch_internal(patrn, strng, ci, sk); */
goto pmatch_top; /* optimize tail recursion */
}
/* case-sensitive wildcard match */
boolean
pmatch(const char *patrn, const char *strng)
{
return pmatch_internal(patrn, strng, FALSE, (const char *) 0);
}
/* case-insensitive wildcard match */
boolean
pmatchi(const char *patrn, const char *strng)
{
return pmatch_internal(patrn, strng, TRUE, (const char *) 0);
}
/*strutil.c*/