1 /* $xMach: strnsubst.c,v 1.3 2002/02/23 02:10:24 jmallett Exp $ */ 2 3 /* 4 * Copyright (c) 2002 J. Mallett. All rights reserved. 5 * You may do whatever you want with this file as long as 6 * the above copyright and this notice remain intact, along 7 * with the following statement: 8 * For the man who taught me vi, and who got too old, too young. 9 */ 10 11 #include <sys/cdefs.h> 12 __FBSDID("$FreeBSD$"); 13 14 #include <err.h> 15 #include <stdbool.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 20 bool strnsubst(char **, const char *, const char *, size_t); 21 22 /* 23 * Replaces str with a string consisting of str with match replaced with 24 * replstr as many times as can be done before the constructed string is 25 * maxsize bytes large. It does not free the string pointed to by str, it 26 * is up to the calling program to be sure that the original contents of 27 * str as well as the new contents are handled in an appropriate manner. 28 * If replstr is NULL, then that internally is changed to a nil-string, so 29 * that we can still pretend to do somewhat meaningful substitution. 30 * 31 * Returns true if truncation was needed to do the replacement, false if 32 * truncation was not required. 33 */ 34 bool 35 strnsubst(char **str, const char *match, const char *replstr, size_t maxsize) 36 { 37 char *s1, *s2, *this; 38 bool error = false; 39 40 s1 = *str; 41 if (s1 == NULL) 42 return false; 43 /* 44 * If maxsize is 0 then set it to the length of s1, because we have 45 * to duplicate s1. XXX we maybe should double-check whether the match 46 * appears in s1. If it doesn't, then we also have to set the length 47 * to the length of s1, to avoid modifying the argument. It may make 48 * sense to check if maxsize is <= strlen(s1), because in that case we 49 * want to return the unmodified string, too. 50 */ 51 if (maxsize == 0) { 52 match = NULL; 53 maxsize = strlen(s1) + 1; 54 } 55 s2 = calloc(1, maxsize); 56 if (s2 == NULL) 57 err(1, "calloc"); 58 59 if (replstr == NULL) 60 replstr = ""; 61 62 if (match == NULL || replstr == NULL || maxsize == strlen(s1)) { 63 strlcpy(s2, s1, maxsize); 64 goto done; 65 } 66 67 for (;;) { 68 this = strstr(s1, match); 69 if (this == NULL) 70 break; 71 if ((strlen(s2) + strlen(s1) + strlen(replstr) - 72 strlen(match) + 1) > maxsize) { 73 strlcat(s2, s1, maxsize); 74 error = true; 75 goto done; 76 } 77 strncat(s2, s1, (uintptr_t)this - (uintptr_t)s1); 78 strcat(s2, replstr); 79 s1 = this + strlen(match); 80 } 81 strcat(s2, s1); 82 done: 83 *str = s2; 84 return error; 85 } 86 87 #ifdef TEST 88 #include <stdio.h> 89 90 int 91 main(void) 92 { 93 char *x, *y, *z, *za; 94 95 x = "{}%$"; 96 strnsubst(&x, "%$", "{} enpury!", 255); 97 y = x; 98 strnsubst(&y, "}{}", "ybir", 255); 99 z = y; 100 strnsubst(&z, "{", "v ", 255); 101 za = z; 102 strnsubst(&z, NULL, za, 255); 103 if (strcmp(z, "v ybir enpury!") == 0) 104 printf("strnsubst() seems to work!\n"); 105 else 106 printf("strnsubst() is broken.\n"); 107 printf("%s\n", z); 108 free(x); 109 free(y); 110 free(z); 111 free(za); 112 return 0; 113 } 114 #endif 115