#include <windows.h>
#define LINE_MAX 4149 // NOTE: this is excluding the terminating null character!
typedef enum {
OK,
INVALID_PARAMS_ERROR,
LINE_TOO_LONG_ERROR
} myerr_t; // whatever
static __inline int char_equal(TCHAR c1, TCHAR c2, int cs)
{
if (cs) return c1 == c2;
return CharLower((LPTSTR) c1) == CharLower((LPTSTR) c2);
}
static ssize_t find_sub(TCHAR *buf, size_t buflen, char *map, const TCHAR *str, size_t len, size_t start, int cs)
{
size_t i;
ssize_t j;
// go through the rest of the buffer, one position at a time
for (i = start; i + len <= buflen; i++) {
// see if we can match the substring here
for (j = len - 1; j >= 0; j--) {
// if any part is not original, skip to the start of the next original part
// we're looping back-to-front so as to skip as much as possible in that case
if (map[i + j]) {
i += j;
break;
}
// actually compare characters
if (!char_equal(buf[i + j], str[j], cs)) break;
}
if (j < 0) return i; // a match; return the starting position
}
return -1; // no match
}
static ssize_t replacex_one(TCHAR *buf, size_t buflen, char *map, const TCHAR *substr, const TCHAR *newstr, int cs)
{
size_t sublen, newlen;
ssize_t pos, tail;
sublen = lstrlen(substr);
newlen = lstrlen(newstr);
if (!sublen) return buflen; // can't replace nothing
pos = 0;
// repeatedly find the next all-original occurrence of the substring to replace
while ((pos = find_sub(buf, buflen, map, substr, sublen, pos, cs)) >= 0) {
// and replace it - at least, if the buffer is big enough
if (buflen - sublen + newlen >= LINE_MAX)
return -1;
// move everything after the substring we found, if necessary
tail = buflen - sublen - pos;
if (sublen != newlen && tail > 0) {
memmove(&buf[pos + newlen], &buf[pos + sublen], sizeof(buf[0]) * tail);
memmove(&map[pos + newlen], &map[pos + sublen], sizeof(map[0]) * tail);
}
// put the new string in the gap, marking it as changed in the map
memcpy(&buf[pos], newstr, sizeof(buf[0]) * newlen);
memset(&map[pos], 1, sizeof(map[0]) * newlen);
buflen += newlen - sublen;
pos += newlen; // the new substring will obviously never match
}
return buflen;
}
/*
* replacex() - implements mIRC's $replacex[cs](buf,substr,newstr,..)
*
* The caller must provide the (null-terminated) input string in 'buf'.
* 'params' must be an array of strings, where the first string is the first
* substr to match, the second string is the first newstr to replace matches
* with, the third string is the second substr, the fourth string is the
* second newstr, etcetera. 'nparams' must be the number of strings in the
* array; it must obviously be a multiple of two, and at least one pair must
* be given. 'cs' indicates case sensitivity (0 = case-insensitive, 1 =
* case-sensitive).
*
* Upon success, OK is returned, and 'buf' contains the (null-terminated)
* result string. Upon failure, an error is returned, and the contents of
* 'buf' are undefined.
*/
myerr_t replacex(TCHAR *buf, const TCHAR *param[], int nparams, int cs)
{
char map[LINE_MAX];
ssize_t buflen;
int i;
// must be given one or more substr/newstr pairs
if (!nparams || nparams % 2) return INVALID_PARAMS_ERROR;
// track which characters have already been replaced before
// initially, everything is still original, so the map is zeroed
buflen = lstrlen(buf);
memset(map, 0, sizeof(map[0]) * buflen);
// replace each pair in the parameter array
for (i = 0; i < nparams; i += 2) {
buflen = replacex_one(buf, buflen, map, param[i], param[i + 1], cs);
if (buflen < 0) return LINE_TOO_LONG_ERROR;
}
// the resulting buffer may not be properly null-terminated, so do that now
buf[buflen] = 0;
return OK;
}
void example(void)
{
TCHAR buf[LINE_MAX+1] = TEXT("1234");
TCHAR *param[] = { TEXT("234"), TEXT("z"), TEXT("123"), TEXT("u") };
if (replacex(buf, param, sizeof(param)/sizeof(param[0]), FALSE) == OK)
printf(TEXT("result string: %s"), buf); // will be "1z"
}