195080Sjmallett/* $xMach: strnsubst.c,v 1.3 2002/02/23 02:10:24 jmallett Exp $ */
295080Sjmallett
395080Sjmallett/*
495080Sjmallett * Copyright (c) 2002 J. Mallett.  All rights reserved.
595080Sjmallett * You may do whatever you want with this file as long as
695080Sjmallett * the above copyright and this notice remain intact, along
795080Sjmallett * with the following statement:
895080Sjmallett * 	For the man who taught me vi, and who got too old, too young.
995080Sjmallett */
1095080Sjmallett
1195080Sjmallett#include <sys/cdefs.h>
1295080Sjmallett__FBSDID("$FreeBSD$");
1395080Sjmallett
1495080Sjmallett#include <err.h>
1595080Sjmallett#include <stdlib.h>
1695080Sjmallett#include <string.h>
1795080Sjmallett#include <unistd.h>
1895080Sjmallett
1995080Sjmallettvoid	strnsubst(char **, const char *, const char *, size_t);
2095080Sjmallett
2195080Sjmallett/*
2295080Sjmallett * Replaces str with a string consisting of str with match replaced with
2395080Sjmallett * replstr as many times as can be done before the constructed string is
2495080Sjmallett * maxsize bytes large.  It does not free the string pointed to by str, it
2595080Sjmallett * is up to the calling program to be sure that the original contents of
2695080Sjmallett * str as well as the new contents are handled in an appropriate manner.
2797619Sjmallett * If replstr is NULL, then that internally is changed to a nil-string, so
2897619Sjmallett * that we can still pretend to do somewhat meaningful substitution.
2995080Sjmallett * No value is returned.
3095080Sjmallett */
3195080Sjmallettvoid
3295080Sjmallettstrnsubst(char **str, const char *match, const char *replstr, size_t maxsize)
3395080Sjmallett{
3495900Sjmallett	char *s1, *s2, *this;
3595080Sjmallett
3695080Sjmallett	s1 = *str;
3795080Sjmallett	if (s1 == NULL)
3895080Sjmallett		return;
39153917Sjmallett	/*
40153917Sjmallett	 * If maxsize is 0 then set it to to the length of s1, because we have
41153917Sjmallett	 * to duplicate s1.  XXX we maybe should double-check whether the match
42153917Sjmallett	 * appears in s1.  If it doesn't, then we also have to set the length
43153917Sjmallett	 * to the length of s1, to avoid modifying the argument.  It may make
44153917Sjmallett	 * sense to check if maxsize is <= strlen(s1), because in that case we
45153917Sjmallett	 * want to return the unmodified string, too.
46153917Sjmallett	 */
47153917Sjmallett	if (maxsize == 0) {
48153917Sjmallett		match = NULL;
49153917Sjmallett		maxsize = strlen(s1) + 1;
50153917Sjmallett	}
51245050Sdelphij	s2 = calloc(1, maxsize);
5295080Sjmallett	if (s2 == NULL)
5395080Sjmallett		err(1, "calloc");
5495080Sjmallett
5597619Sjmallett	if (replstr == NULL)
5697619Sjmallett		replstr = "";
5797619Sjmallett
5895900Sjmallett	if (match == NULL || replstr == NULL || maxsize == strlen(s1)) {
5995898Sjmallett		strlcpy(s2, s1, maxsize);
6095898Sjmallett		goto done;
6195898Sjmallett	}
6295898Sjmallett
6395080Sjmallett	for (;;) {
6495080Sjmallett		this = strstr(s1, match);
6595080Sjmallett		if (this == NULL)
6695080Sjmallett			break;
67136664Scperciva		if ((strlen(s2) + strlen(s1) + strlen(replstr) -
68136664Scperciva		    strlen(match) + 1) > maxsize) {
6995080Sjmallett			strlcat(s2, s1, maxsize);
7095080Sjmallett			goto done;
7195080Sjmallett		}
7295080Sjmallett		strncat(s2, s1, (uintptr_t)this - (uintptr_t)s1);
7395080Sjmallett		strcat(s2, replstr);
7495080Sjmallett		s1 = this + strlen(match);
7595080Sjmallett	}
7695080Sjmallett	strcat(s2, s1);
7795080Sjmallettdone:
7895080Sjmallett	*str = s2;
7995080Sjmallett	return;
8095080Sjmallett}
8195080Sjmallett
8295080Sjmallett#ifdef TEST
8395080Sjmallett#include <stdio.h>
8495080Sjmallett
8595080Sjmallettint
8695080Sjmallettmain(void)
8795080Sjmallett{
8898616Sjmallett	char *x, *y, *z, *za;
8995080Sjmallett
9098616Sjmallett	x = "{}%$";
9198616Sjmallett	strnsubst(&x, "%$", "{} enpury!", 255);
9298616Sjmallett	y = x;
9398616Sjmallett	strnsubst(&y, "}{}", "ybir", 255);
9498616Sjmallett	z = y;
9598616Sjmallett	strnsubst(&z, "{", "v ", 255);
9698616Sjmallett	za = z;
9798616Sjmallett	strnsubst(&z, NULL, za, 255);
9898616Sjmallett	if (strcmp(z, "v ybir enpury!") == 0)
9998616Sjmallett		printf("strnsubst() seems to work!\n");
10098616Sjmallett	else
10198616Sjmallett		printf("strnsubst() is broken.\n");
10298616Sjmallett	printf("%s\n", z);
10395080Sjmallett	free(x);
10498616Sjmallett	free(y);
10598616Sjmallett	free(z);
10698616Sjmallett	free(za);
10795080Sjmallett	return 0;
10895080Sjmallett}
10995080Sjmallett#endif
110