1*d35fae1dSkre /* $NetBSD: expandm.c,v 1.11 2019/01/23 02:48:48 kre Exp $ */ 2de284403Schristos 3de284403Schristos /*- 472194717Skre * Copyright (c) 2019 The NetBSD Foundation, Inc. 5de284403Schristos * All rights reserved. 6de284403Schristos * 7de284403Schristos * This code is derived from software contributed to The NetBSD Foundation 8de284403Schristos * by Christos Zoulas. 9de284403Schristos * 10de284403Schristos * Redistribution and use in source and binary forms, with or without 11de284403Schristos * modification, are permitted provided that the following conditions 12de284403Schristos * are met: 13de284403Schristos * 1. Redistributions of source code must retain the above copyright 14de284403Schristos * notice, this list of conditions and the following disclaimer. 15de284403Schristos * 2. Redistributions in binary form must reproduce the above copyright 16de284403Schristos * notice, this list of conditions and the following disclaimer in the 17de284403Schristos * documentation and/or other materials provided with the distribution. 18de284403Schristos * 19de284403Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20de284403Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21de284403Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22de284403Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23de284403Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24de284403Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25de284403Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26de284403Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27de284403Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28de284403Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29de284403Schristos * POSSIBILITY OF SUCH DAMAGE. 30de284403Schristos */ 31de284403Schristos #include <sys/cdefs.h> 32*d35fae1dSkre __RCSID("$NetBSD: expandm.c,v 1.11 2019/01/23 02:48:48 kre Exp $"); 33de284403Schristos 3451621f70Schristos #include <limits.h> 35de284403Schristos #include <stdio.h> 36de284403Schristos #include <string.h> 37de284403Schristos #include <stdlib.h> 38de284403Schristos #include <errno.h> 39de284403Schristos 40de284403Schristos #include "expandm.h" 41de284403Schristos 4251621f70Schristos #ifdef TEST 4351621f70Schristos #undef INT_MAX 4451621f70Schristos #define INT_MAX 31 4551621f70Schristos #endif 4651621f70Schristos 4751621f70Schristos 480f2ce139Skre const char * __attribute__((__format_arg__(1))) 49c5cf555eSchristos expandm(const char *fmt, const char *sf, char **rbuf) 50de284403Schristos { 51f1ee8649Skre const int err = errno; 52f1ee8649Skre const char *e = NULL; 53de284403Schristos char *buf, *m, *nbuf; 54de284403Schristos const char *ptr; 55de284403Schristos 5672194717Skre buf = NULL; 5772194717Skre for (ptr = fmt; (m = strstr(ptr, "%m")) != NULL; ptr = m + 2) { 58de284403Schristos size_t cnt = 0; 59b0c40852Skre size_t nlen; 6051621f70Schristos 61de284403Schristos for (char *p = m; p >= ptr && *p == '%'; p--) 62de284403Schristos cnt++; 6351621f70Schristos 64b0c40852Skre nlen = (size_t)(m - ptr); 65b31f5e70Schristos /* 66b31f5e70Schristos * we can't exceed INT_MAX because int is used as 67b31f5e70Schristos * a format width 68b31f5e70Schristos */ 69b31f5e70Schristos if (__predict_false(nlen >= INT_MAX)) { 70b31f5e70Schristos size_t blen = buf ? strlen(buf) : 0; 71*d35fae1dSkre size_t tlen; 72*d35fae1dSkre 73*d35fae1dSkre /* 74*d35fae1dSkre * if we would overflow a ptrdiff_t when computing 75*d35fae1dSkre * tlen, then don't bother. The format string is 76*d35fae1dSkre * simply too large to be converted. 77*d35fae1dSkre */ 78*d35fae1dSkre if (blen >= PTRDIFF_MAX || 79*d35fae1dSkre nlen >= PTRDIFF_MAX - blen || 80*d35fae1dSkre nlen >= SIZE_T_MAX - blen) 81*d35fae1dSkre goto out; 82*d35fae1dSkre 83*d35fae1dSkre tlen = nlen + blen; 8451621f70Schristos 85b31f5e70Schristos /* 86b31f5e70Schristos * We can't exceed PTRDIFF_MAX because we would 87b31f5e70Schristos * not be able to address the pointers 88b31f5e70Schristos */ 89b0c40852Skre if (tlen >= PTRDIFF_MAX) 90b31f5e70Schristos goto out; 91b31f5e70Schristos 92b31f5e70Schristos nbuf = realloc(buf, tlen + 1); 9351621f70Schristos if (nbuf == NULL) 9451621f70Schristos goto out; 9551621f70Schristos 9651621f70Schristos memcpy(nbuf + blen, ptr, nlen); 97b31f5e70Schristos nbuf[tlen] = '\0'; 9851621f70Schristos ptr += nlen; 9951621f70Schristos buf = nbuf; 10051621f70Schristos } 10151621f70Schristos 102f1ee8649Skre if (__predict_true(e == NULL && (cnt & 1) != 0)) 103f1ee8649Skre e = strerror(err); 104de284403Schristos if (asprintf(&nbuf, "%s%.*s%s", buf ? buf : "", 105d501129cSchristos (int)(m - ptr), ptr, (cnt & 1) ? e : "%m") == -1) 106de284403Schristos goto out; 107de284403Schristos free(buf); 108de284403Schristos buf = nbuf; 109de284403Schristos } 110de284403Schristos 111de284403Schristos if (asprintf(&nbuf, "%s%s%s", buf ? buf : "", ptr, sf ? sf : "") == -1) 112de284403Schristos goto out; 113de284403Schristos 114de284403Schristos free(buf); 115c5cf555eSchristos if (rbuf) 116bd7d4285Skre *rbuf = nbuf; 117f1ee8649Skre errno = err; 118de284403Schristos return nbuf; 119de284403Schristos out: 120de284403Schristos free(buf); 121c5cf555eSchristos if (rbuf) 122c5cf555eSchristos *rbuf = NULL; 123f1ee8649Skre errno = err; 1240f2ce139Skre return fmt; 125de284403Schristos } 126de284403Schristos 127de284403Schristos #ifdef TEST 128de284403Schristos int 129de284403Schristos main(int argc, char *argv[]) 130de284403Schristos { 131de284403Schristos errno = ERANGE; 1320f2ce139Skre printf("%s\n", expandm(argc > 1 ? argv[1] : "Message %%m=%m: %%%m%%", 1330f2ce139Skre "...", NULL)); 134de284403Schristos return 0; 135de284403Schristos } 136de284403Schristos #endif 137