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 * $FreeBSD: head/usr.bin/xargs/strnsubst.c 327230 2017-12-27 03:23:01Z eadler $ 11 */ 12 13 #include <err.h> 14 #include <stdbool.h> 15 #include <stdint.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 * Returns true if truncation was needed to do the replacement, false if 31 * truncation was not required. 32 */ 33 bool 34 strnsubst(char **str, const char *match, const char *replstr, size_t maxsize) 35 { 36 char *s1, *s2, *this; 37 bool error = false; 38 39 s1 = *str; 40 if (s1 == NULL) 41 return false; 42 /* 43 * If maxsize is 0 then set it to the length of s1, because we have 44 * to duplicate s1. XXX we maybe should double-check whether the match 45 * appears in s1. If it doesn't, then we also have to set the length 46 * to the length of s1, to avoid modifying the argument. It may make 47 * sense to check if maxsize is <= strlen(s1), because in that case we 48 * want to return the unmodified string, too. 49 */ 50 if (maxsize == 0) { 51 match = NULL; 52 maxsize = strlen(s1) + 1; 53 } 54 s2 = calloc(1, maxsize); 55 if (s2 == NULL) 56 err(1, "calloc"); 57 58 if (replstr == NULL) 59 replstr = ""; 60 61 if (match == NULL || replstr == NULL || maxsize == strlen(s1)) { 62 strlcpy(s2, s1, maxsize); 63 goto done; 64 } 65 66 for (;;) { 67 this = strstr(s1, match); 68 if (this == NULL) 69 break; 70 if ((strlen(s2) + strlen(s1) + strlen(replstr) - 71 strlen(match) + 1) > maxsize) { 72 strlcat(s2, s1, maxsize); 73 error = true; 74 goto done; 75 } 76 strncat(s2, s1, (uintptr_t)this - (uintptr_t)s1); 77 strcat(s2, replstr); 78 s1 = this + strlen(match); 79 } 80 strcat(s2, s1); 81 done: 82 *str = s2; 83 return error; 84 } 85 86 #ifdef TEST 87 #include <stdio.h> 88 89 int 90 main(void) 91 { 92 char *x, *y, *z, *za; 93 94 x = "{}%$"; 95 strnsubst(&x, "%$", "{} enpury!", 255); 96 y = x; 97 strnsubst(&y, "}{}", "ybir", 255); 98 z = y; 99 strnsubst(&z, "{", "v ", 255); 100 za = z; 101 strnsubst(&z, NULL, za, 255); 102 if (strcmp(z, "v ybir enpury!") == 0) 103 printf("strnsubst() seems to work!\n"); 104 else 105 printf("strnsubst() is broken.\n"); 106 printf("%s\n", z); 107 free(x); 108 free(y); 109 free(z); 110 free(za); 111 return 0; 112 } 113 #endif 114