Blob Blame History Raw
/**************************************************************/
/*  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;
}