split some code into separate files
new .h files: hacklib.h selvar.h stairs.h
new .c files: calendar.c, getpos.c, report.c, selvar.c, stairs.c,
strutil.c, wizcmds.c
cleanup of hacklib.c and mdlib.c
hacklib contains functions that do not have to link with the core
relocate wiz commands from cmd.c to wizcmds.c
relocate CRASHREPORT stuff to report.c
relocate getpos stuff from do_name.c to getpos.c
remove temporary struct definition from extern.h
cross-compile PRE-section split into cross-pre1.370 and cross-pre2.370
Windows sys/windows/Makefile.nmake and sys/windows/Makefile.mingw32 and
visual studio project file updates
Unix sys/unix/Makefile.src, sys/unix/Makefile.utl
populate selvar.c and selvar.h
build on MS-DOS (not cross-compile) Makefile updates
for sys/msdos/Makefile.GCC (untested)
vms updates for above (untested)
This commit is contained in:
158
src/strutil.c
Normal file
158
src/strutil.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/* 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 occurences 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;
|
||||
}
|
||||
|
||||
static boolean pmatch_internal(const char *, const char *, boolean,
|
||||
const char *);
|
||||
/* guts of pmatch(), pmatchi(), and pmatchz();
|
||||
match a string against a pattern */
|
||||
static 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*/
|
||||
Reference in New Issue
Block a user