1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * String utility functions with dynamic memory management. 14 */ 15 16 /* 17 * Copyright 2019 Joyent, Inc. 18 */ 19 20 #include <stdlib.h> 21 #include <err.h> 22 #include <errno.h> 23 #include <string.h> 24 #include <stdio.h> 25 #include <stdarg.h> 26 #include <sys/debug.h> 27 28 #include "libcustr.h" 29 30 /* 31 * libcustr is used by some things in usr/src/tools. If we are building 32 * on an older platform, __unused might not be defined on the build host. 33 * We define it here if needed. 34 */ 35 #ifndef __unused 36 #if __GNUC_VERSION >= 20700 37 #define __unused __attribute__((_unused__)) 38 #else 39 #define __unused 40 #endif /* __GNUC_VERSION */ 41 #endif /* __unused */ 42 43 typedef enum { 44 CUSTR_FIXEDBUF = 0x01 45 } custr_flags_t; 46 47 struct custr { 48 size_t cus_strlen; 49 size_t cus_datalen; 50 char *cus_data; 51 custr_flags_t cus_flags; 52 custr_alloc_t *cus_alloc; 53 }; 54 #define CUSTR_ALLOC(_cus, _len) \ 55 (_cus)->cus_alloc->cua_ops->custr_ao_alloc((_cus)->cus_alloc, (_len)) 56 #define CUSTR_FREE(_cus, _p, _len) \ 57 (_cus)->cus_alloc->cua_ops->custr_ao_free((_cus)->cus_alloc, \ 58 (_p), (_len)) 59 60 #define STRING_CHUNK_SIZE 64 61 62 static void *custr_def_alloc(custr_alloc_t *, size_t); 63 static void custr_def_free(custr_alloc_t *, void *, size_t); 64 65 static custr_alloc_ops_t custr_alloc_ops_default = { 66 NULL, /* custr_ao_init */ 67 NULL, /* custr_ao_fini */ 68 custr_def_alloc, /* custr_ao_alloc */ 69 custr_def_free /* custr_ao_free */ 70 }; 71 72 static custr_alloc_t custr_alloc_default = { 73 CUSTR_VERSION, /* cua_version */ 74 &custr_alloc_ops_default, /* cua_ops */ 75 NULL /* cua_arg */ 76 }; 77 78 void 79 custr_reset(custr_t *cus) 80 { 81 if (cus->cus_data == NULL) 82 return; 83 84 cus->cus_strlen = 0; 85 cus->cus_data[0] = '\0'; 86 } 87 88 size_t 89 custr_len(custr_t *cus) 90 { 91 return (cus->cus_strlen); 92 } 93 94 const char * 95 custr_cstr(custr_t *cus) 96 { 97 if (cus->cus_data == NULL) { 98 VERIFY(cus->cus_strlen == 0); 99 VERIFY(cus->cus_datalen == 0); 100 101 /* 102 * This function should never return NULL. If no buffer has 103 * been allocated, return a pointer to a zero-length string. 104 */ 105 return (""); 106 } 107 return (cus->cus_data); 108 } 109 110 int 111 custr_append_vprintf(custr_t *cus, const char *fmt, va_list ap) 112 { 113 int len = vsnprintf(NULL, 0, fmt, ap); 114 size_t chunksz = STRING_CHUNK_SIZE; 115 116 if (len < 0) { 117 return (len); 118 } 119 120 while (chunksz < len) { 121 chunksz *= 2; 122 } 123 124 if (len + cus->cus_strlen + 1 >= cus->cus_datalen) { 125 char *new_data; 126 size_t new_datalen = cus->cus_datalen + chunksz; 127 128 if (cus->cus_flags & CUSTR_FIXEDBUF) { 129 errno = EOVERFLOW; 130 return (-1); 131 } 132 133 /* 134 * Allocate replacement memory: 135 */ 136 if ((new_data = CUSTR_ALLOC(cus, new_datalen)) == NULL) { 137 return (-1); 138 } 139 140 /* 141 * Copy existing data into replacement memory and free 142 * the old memory. 143 */ 144 if (cus->cus_data != NULL) { 145 (void) memcpy(new_data, cus->cus_data, 146 cus->cus_strlen + 1); 147 CUSTR_FREE(cus, cus->cus_data, cus->cus_datalen); 148 } 149 150 /* 151 * Swap in the replacement buffer: 152 */ 153 cus->cus_data = new_data; 154 cus->cus_datalen = new_datalen; 155 } 156 /* 157 * Append new string to existing string: 158 */ 159 if ((len = vsnprintf(cus->cus_data + cus->cus_strlen, 160 cus->cus_datalen - cus->cus_strlen, fmt, ap)) < 0) { 161 return (len); 162 } 163 cus->cus_strlen += len; 164 165 return (0); 166 } 167 168 int 169 custr_appendc(custr_t *cus, char newc) 170 { 171 return (custr_append_printf(cus, "%c", newc)); 172 } 173 174 int 175 custr_append_printf(custr_t *cus, const char *fmt, ...) 176 { 177 va_list ap; 178 int ret; 179 180 va_start(ap, fmt); 181 ret = custr_append_vprintf(cus, fmt, ap); 182 va_end(ap); 183 184 return (ret); 185 } 186 187 int 188 custr_append(custr_t *cus, const char *name) 189 { 190 return (custr_append_printf(cus, "%s", name)); 191 } 192 193 int 194 custr_alloc_init(custr_alloc_t *cua, const custr_alloc_ops_t *ops, ...) 195 { 196 int ret = 0; 197 198 if (cua->cua_version != CUSTR_VERSION || ops->custr_ao_alloc == NULL || 199 ops->custr_ao_free == NULL) { 200 errno = EINVAL; 201 return (-1); 202 } 203 204 cua->cua_ops = ops; 205 cua->cua_arg = NULL; 206 207 if (ops->custr_ao_init != NULL) { 208 va_list ap; 209 210 va_start(ap, ops); 211 ret = ops->custr_ao_init(cua, ap); 212 va_end(ap); 213 } 214 215 return ((ret == 0) ? 0 : -1); 216 } 217 218 void 219 custr_alloc_fini(custr_alloc_t *cua) 220 { 221 if (cua->cua_ops->custr_ao_fini != NULL) 222 cua->cua_ops->custr_ao_fini(cua); 223 } 224 225 int 226 custr_xalloc(custr_t **cus, custr_alloc_t *cao) 227 { 228 custr_t *t; 229 230 if (cao == NULL) 231 cao = &custr_alloc_default; 232 233 if ((t = cao->cua_ops->custr_ao_alloc(cao, sizeof (*t))) == NULL) { 234 *cus = NULL; 235 return (-1); 236 } 237 (void) memset(t, 0, sizeof (*t)); 238 239 t->cus_alloc = cao; 240 *cus = t; 241 return (0); 242 } 243 244 int 245 custr_alloc(custr_t **cus) 246 { 247 return (custr_xalloc(cus, NULL)); 248 } 249 250 int 251 custr_xalloc_buf(custr_t **cus, void *buf, size_t buflen, custr_alloc_t *cao) 252 { 253 int ret; 254 255 if (buflen == 0 || buf == NULL) { 256 errno = EINVAL; 257 return (-1); 258 } 259 260 if ((ret = custr_xalloc(cus, cao)) != 0) 261 return (ret); 262 263 (*cus)->cus_data = buf; 264 (*cus)->cus_datalen = buflen; 265 (*cus)->cus_strlen = 0; 266 (*cus)->cus_flags = CUSTR_FIXEDBUF; 267 (*cus)->cus_data[0] = '\0'; 268 269 return (0); 270 } 271 272 int 273 custr_alloc_buf(custr_t **cus, void *buf, size_t buflen) 274 { 275 return (custr_xalloc_buf(cus, buf, buflen, NULL)); 276 } 277 278 void 279 custr_free(custr_t *cus) 280 { 281 custr_alloc_t *cao; 282 283 if (cus == NULL) 284 return; 285 286 if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0) 287 CUSTR_FREE(cus, cus->cus_data, cus->cus_datalen); 288 289 cao = cus->cus_alloc; 290 cao->cua_ops->custr_ao_free(cao, cus, sizeof (*cus)); 291 } 292 293 /*ARGSUSED*/ 294 static void * 295 custr_def_alloc(custr_alloc_t *cao __unused, size_t len) 296 { 297 return (malloc(len)); 298 } 299 300 /*ARGSUSED*/ 301 static void 302 custr_def_free(custr_alloc_t *cao __unused, void *p, size_t len __unused) 303 { 304 free(p); 305 } 306