/**************************************************************/
/* tpax: a topological pax implementation */
/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
#include <regex.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <tpax/tpax.h>
#include "tpax_driver_impl.h"
static int tpax_backref_idx(const char c)
{
return ((c >= '1') && (c <= '9')) ? c - '0' : 0;
}
int tpax_util_path_replstr(
char * dstpath,
const char * srcpath,
const char * replstr,
const regex_t * regex,
size_t buflen,
int flags)
{
int ret;
int idx;
regoff_t ro;
const char * ch;
char * dst;
size_t explen;
regmatch_t pmatch[11];
/* attempt to match */
switch (regexec(regex,srcpath,11,pmatch,0)) {
case 0:
break;
case REG_NOMATCH:
return 0;
default:
return -1;
}
/* copy bytes leading up to match */
if (buflen <= (explen = pmatch[0].rm_so)) {
errno = ENOBUFS;
return -1;
}
for (ro=0,dst=dstpath; ro<pmatch[0].rm_so; ro++)
*dst++ = srcpath[ro];
buflen -= explen;
/* copy replacement string */
for (ch=replstr,ret=0; buflen && *ch; ch++) {
/* <ampersand> stands for the entire matched string */
if (ch[0] == '&') {
idx = 0;
/* back-reference semantics: a matched subexpression or an empty string */
} else if ((ch[0] == '\\') && (idx = tpax_backref_idx(ch[1]))) {
if (pmatch[idx].rm_so < 0)
idx = -1;
ch++;
/* all other escaped characters */
} else if (ch[0] == '\\') {
*dst++ = *++ch;
idx = -1;
buflen--;
/* all other characters */
} else {
*dst++ = *ch;
idx = -1;
buflen--;
}
/* copy matched string or matched subexpression, if any */
if (idx >= 0) {
if (buflen <= (explen = (pmatch[idx].rm_eo - pmatch[idx].rm_so))) {
errno = ENOBUFS;
return -1;
}
for (ro=pmatch[idx].rm_so; ro<pmatch[idx].rm_eo; ro++)
*dst++ = srcpath[ro];
buflen -= explen;
}
}
/* replace further occurrences as needed */
if ((flags & TPAX_REPL_GLOBAL) && srcpath[pmatch[0].rm_eo])
ret = tpax_util_path_replstr(
dst,&srcpath[pmatch[0].rm_eo],replstr,
regex,buflen,flags);
if (ret < 0)
return -1;
/* copy remaining, non-matching bytes as needed */
if (ret == 0) {
for (ch=&srcpath[pmatch[0].rm_eo]; *ch; ch++)
*dst++ = *ch;
*dst = '\0';
}
/* all done */
ret += (dst - dstpath);
return ret;
}