1 /*- 2 * Copyright (c) 1996 Peter Wemm <peter@freebsd.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #if defined(LIBC_RCS) && !defined(lint) 27 static char rcsid[] = "$Id$"; 28 #endif /* LIBC_RCS and not lint */ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 33 #if __STDC__ 34 #include <stdarg.h> 35 #else 36 #include <varargs.h> 37 #endif 38 39 #define CHUNK_SPARE 128 /* how much spare to allocate to avoid realloc calls */ 40 41 struct bufcookie { 42 char *base; /* start of buffer */ 43 size_t size; 44 size_t left; 45 }; 46 47 static int writehook __P((void *cookie, const char *, int)); 48 49 static int 50 writehook(cookie, buf, len) 51 void *cookie; 52 const char *buf; 53 int len; 54 { 55 struct bufcookie *h = (struct bufcookie *)cookie; 56 char *newbuf; 57 58 if (len == 0) 59 return 0; 60 61 if (len > h->left) { 62 /* grow malloc region */ 63 /* 64 * XXX this is linearly expanded, which is slow for obscenely 65 * large strings. 66 */ 67 h->left = h->left + len + CHUNK_SPARE; 68 h->size = h->size + len + CHUNK_SPARE; 69 newbuf = realloc(h->base, h->size); 70 if (newbuf == NULL) { 71 free(h->base); 72 h->base = NULL; 73 return (-1); 74 } else 75 h->base = newbuf; 76 } 77 /* "write" it */ 78 (void)memcpy(h->base + h->size - h->left, buf, (size_t)len); 79 h->left -= len; 80 return (len); 81 } 82 83 84 int 85 vasprintf(str, fmt, ap) 86 char **str; 87 const char *fmt; 88 va_list ap; 89 { 90 int ret; 91 FILE *f; 92 struct bufcookie h; 93 94 h.base = malloc(CHUNK_SPARE); 95 if (h.base == NULL) 96 return (-1); 97 h.size = CHUNK_SPARE; 98 h.left = CHUNK_SPARE; 99 100 f = funopen(&h, NULL, writehook, NULL, NULL); 101 if (f == NULL) { 102 free(h.base); 103 return (-1); 104 } 105 ret = vfprintf(f, fmt, ap); 106 fclose(f); 107 if (ret < 0) { 108 free(h.base); 109 return (-1); 110 } 111 if (h.base == NULL) /* failed to realloc in writehook */ 112 return (-1); 113 114 h.base[h.size - h.left] = '\0'; 115 *str = realloc(h.base, (size_t)(h.size - h.left + 1)); 116 if (*str == NULL) /* failed to realloc it to actual size */ 117 *str = h.base; /* return oversize buffer */ 118 return (ret); 119 } 120